blob: ddf6d2c4ab15baa6873437e36bb1f84a0918afae [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
paulhua262cc12019-08-12 16:25:11 +080019import android.content.Context;
Irfan Sheriff75006652012-04-17 23:15:29 -070020import android.content.Intent;
paulhub2225702021-11-17 09:35:33 +080021import android.content.pm.PackageManager;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090022import android.net.ConnectivityManager;
23import android.net.LinkProperties;
24import android.net.Network;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070025import android.net.nsd.INsdManager;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090026import android.net.nsd.INsdManagerCallback;
27import android.net.nsd.INsdServiceConnector;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070028import android.net.nsd.NsdManager;
paulhua262cc12019-08-12 16:25:11 +080029import android.net.nsd.NsdServiceInfo;
Hugo Benichi803a2f02017-04-24 11:35:06 +090030import android.os.Handler;
paulhua262cc12019-08-12 16:25:11 +080031import android.os.HandlerThread;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090032import android.os.IBinder;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070033import android.os.Message;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090034import android.os.RemoteException;
Dianne Hackborn692107e2012-08-29 18:32:08 -070035import android.os.UserHandle;
Philip P. Moltmannbbb41dd2016-03-16 10:15:39 -070036import android.util.Base64;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090037import android.util.Log;
38import android.util.Pair;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070039import android.util.SparseArray;
Hugo Benichid2552ae2017-04-11 14:42:47 +090040import android.util.SparseIntArray;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070041
paulhua262cc12019-08-12 16:25:11 +080042import com.android.internal.annotations.VisibleForTesting;
paulhua262cc12019-08-12 16:25:11 +080043import com.android.internal.util.State;
44import com.android.internal.util.StateMachine;
Chalard Jean339c0102020-08-19 16:07:22 +090045import com.android.net.module.util.DnsSdTxtRecord;
paulhua262cc12019-08-12 16:25:11 +080046
Irfan Sheriff77ec5582012-03-22 17:01:39 -070047import java.io.FileDescriptor;
48import java.io.PrintWriter;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070049import java.net.InetAddress;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090050import java.net.NetworkInterface;
51import java.net.SocketException;
52import java.net.UnknownHostException;
Hugo Benichi6d706442017-04-24 16:19:58 +090053import java.util.Arrays;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070054import java.util.HashMap;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070055import java.util.concurrent.CountDownLatch;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070056
Irfan Sheriff77ec5582012-03-22 17:01:39 -070057/**
58 * Network Service Discovery Service handles remote service discovery operation requests by
59 * implementing the INsdManager interface.
60 *
61 * @hide
62 */
63public class NsdService extends INsdManager.Stub {
64 private static final String TAG = "NsdService";
65 private static final String MDNS_TAG = "mDnsConnector";
66
Hugo Benichi32be63d2017-04-05 14:06:11 +090067 private static final boolean DBG = true;
Luke Huang92860f92021-06-23 06:29:30 +000068 private static final long CLEANUP_DELAY_MS = 10000;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090069 private static final int IFACE_IDX_ANY = 0;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070070
Hugo Benichi32be63d2017-04-05 14:06:11 +090071 private final Context mContext;
Hugo Benichi32be63d2017-04-05 14:06:11 +090072 private final NsdStateMachine mNsdStateMachine;
Hugo Benichi6d706442017-04-24 16:19:58 +090073 private final DaemonConnection mDaemon;
74 private final NativeCallbackReceiver mDaemonCallback;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070075
76 /**
77 * Clients receiving asynchronous messages
78 */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090079 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff77ec5582012-03-22 17:01:39 -070080
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070081 /* A map from unique id to client info */
Hugo Benichi32be63d2017-04-05 14:06:11 +090082 private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070083
Luke Huang05298582021-06-13 16:52:05 +000084 private final long mCleanupDelayMs;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070085
Hugo Benichi32be63d2017-04-05 14:06:11 +090086 private static final int INVALID_ID = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070087 private int mUniqueId = 1;
Luke Huangf7277ed2021-07-12 21:15:10 +080088 // The count of the connected legacy clients.
89 private int mLegacyClientCount = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070090
Irfan Sheriff75006652012-04-17 23:15:29 -070091 private class NsdStateMachine extends StateMachine {
92
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070093 private final DefaultState mDefaultState = new DefaultState();
94 private final DisabledState mDisabledState = new DisabledState();
95 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff75006652012-04-17 23:15:29 -070096
97 @Override
Wink Saville358f5d42012-05-29 12:40:46 -070098 protected String getWhatToString(int what) {
Hugo Benichi32be63d2017-04-05 14:06:11 +090099 return NsdManager.nameOf(what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700100 }
101
Luke Huang92860f92021-06-23 06:29:30 +0000102 private void maybeStartDaemon() {
Luke Huang05298582021-06-13 16:52:05 +0000103 mDaemon.maybeStart();
104 maybeScheduleStop();
105 }
106
Luke Huang92860f92021-06-23 06:29:30 +0000107 private boolean isAnyRequestActive() {
108 return mIdToClientInfoMap.size() != 0;
109 }
110
111 private void scheduleStop() {
112 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
113 }
114 private void maybeScheduleStop() {
Luke Huangf7277ed2021-07-12 21:15:10 +0800115 // The native daemon should stay alive and can't be cleanup
116 // if any legacy client connected.
117 if (!isAnyRequestActive() && mLegacyClientCount == 0) {
Luke Huang92860f92021-06-23 06:29:30 +0000118 scheduleStop();
Luke Huang05298582021-06-13 16:52:05 +0000119 }
120 }
121
Luke Huang92860f92021-06-23 06:29:30 +0000122 private void cancelStop() {
Luke Huang05298582021-06-13 16:52:05 +0000123 this.removeMessages(NsdManager.DAEMON_CLEANUP);
124 }
125
Hugo Benichi803a2f02017-04-24 11:35:06 +0900126 NsdStateMachine(String name, Handler handler) {
127 super(name, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700128 addState(mDefaultState);
129 addState(mDisabledState, mDefaultState);
130 addState(mEnabledState, mDefaultState);
paulhu5568f452021-11-30 13:31:29 +0800131 State initialState = mEnabledState;
Hugo Benichi912db992017-04-24 16:41:03 +0900132 setInitialState(initialState);
Wink Saville358f5d42012-05-29 12:40:46 -0700133 setLogRecSize(25);
Irfan Sheriff75006652012-04-17 23:15:29 -0700134 }
135
136 class DefaultState extends State {
137 @Override
138 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900139 final ClientInfo cInfo;
140 final int clientId = msg.arg2;
Irfan Sheriff75006652012-04-17 23:15:29 -0700141 switch (msg.what) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900142 case NsdManager.REGISTER_CLIENT:
143 final Pair<NsdServiceConnector, INsdManagerCallback> arg =
144 (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj;
145 final INsdManagerCallback cb = arg.second;
146 try {
147 cb.asBinder().linkToDeath(arg.first, 0);
148 cInfo = new ClientInfo(cb);
149 mClients.put(arg.first, cInfo);
150 } catch (RemoteException e) {
151 Log.w(TAG, "Client " + clientId + " has already died");
Irfan Sheriff75006652012-04-17 23:15:29 -0700152 }
153 break;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900154 case NsdManager.UNREGISTER_CLIENT:
155 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
156 cInfo = mClients.remove(connector);
Dave Plattfeff2af2014-03-07 14:48:22 -0800157 if (cInfo != null) {
158 cInfo.expungeAllRequests();
Luke Huangf7277ed2021-07-12 21:15:10 +0800159 if (cInfo.isLegacy()) {
160 mLegacyClientCount -= 1;
161 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800162 }
Luke Huangf7277ed2021-07-12 21:15:10 +0800163 maybeScheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700164 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700165 case NsdManager.DISCOVER_SERVICES:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900166 cInfo = getClientInfoForReply(msg);
167 if (cInfo != null) {
168 cInfo.onDiscoverServicesFailed(
169 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
170 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700171 break;
172 case NsdManager.STOP_DISCOVERY:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900173 cInfo = getClientInfoForReply(msg);
174 if (cInfo != null) {
175 cInfo.onStopDiscoveryFailed(
176 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
177 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700178 break;
179 case NsdManager.REGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900180 cInfo = getClientInfoForReply(msg);
181 if (cInfo != null) {
182 cInfo.onRegisterServiceFailed(
183 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
184 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700185 break;
186 case NsdManager.UNREGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900187 cInfo = getClientInfoForReply(msg);
188 if (cInfo != null) {
189 cInfo.onUnregisterServiceFailed(
190 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
191 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700192 break;
193 case NsdManager.RESOLVE_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900194 cInfo = getClientInfoForReply(msg);
195 if (cInfo != null) {
196 cInfo.onResolveServiceFailed(
197 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
198 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700199 break;
Luke Huang05298582021-06-13 16:52:05 +0000200 case NsdManager.DAEMON_CLEANUP:
201 mDaemon.maybeStop();
202 break;
Luke Huangf7277ed2021-07-12 21:15:10 +0800203 // This event should be only sent by the legacy (target SDK < S) clients.
204 // Mark the sending client as legacy.
205 case NsdManager.DAEMON_STARTUP:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900206 cInfo = getClientInfoForReply(msg);
Luke Huangf7277ed2021-07-12 21:15:10 +0800207 if (cInfo != null) {
208 cancelStop();
209 cInfo.setLegacy();
210 mLegacyClientCount += 1;
211 maybeStartDaemon();
212 }
213 break;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700214 case NsdManager.NATIVE_DAEMON_EVENT:
Irfan Sheriff75006652012-04-17 23:15:29 -0700215 default:
paulhub2225702021-11-17 09:35:33 +0800216 Log.e(TAG, "Unhandled " + msg);
Irfan Sheriff75006652012-04-17 23:15:29 -0700217 return NOT_HANDLED;
218 }
219 return HANDLED;
220 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900221
222 private ClientInfo getClientInfoForReply(Message msg) {
223 final ListenerArgs args = (ListenerArgs) msg.obj;
224 return mClients.get(args.connector);
225 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700226 }
227
228 class DisabledState extends State {
229 @Override
230 public void enter() {
231 sendNsdStateChangeBroadcast(false);
232 }
233
234 @Override
235 public boolean processMessage(Message msg) {
236 switch (msg.what) {
237 case NsdManager.ENABLE:
238 transitionTo(mEnabledState);
239 break;
240 default:
241 return NOT_HANDLED;
242 }
243 return HANDLED;
244 }
245 }
246
247 class EnabledState extends State {
248 @Override
249 public void enter() {
250 sendNsdStateChangeBroadcast(true);
Irfan Sheriff75006652012-04-17 23:15:29 -0700251 }
252
253 @Override
254 public void exit() {
Luke Huang05298582021-06-13 16:52:05 +0000255 // TODO: it is incorrect to stop the daemon without expunging all requests
256 // and sending error callbacks to clients.
Luke Huang92860f92021-06-23 06:29:30 +0000257 scheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700258 }
259
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700260 private boolean requestLimitReached(ClientInfo clientInfo) {
261 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
paulhub2225702021-11-17 09:35:33 +0800262 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700263 return true;
264 }
265 return false;
266 }
267
Dave Plattfeff2af2014-03-07 14:48:22 -0800268 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700269 clientInfo.mClientIds.put(clientId, globalId);
Dave Plattfeff2af2014-03-07 14:48:22 -0800270 clientInfo.mClientRequests.put(clientId, what);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700271 mIdToClientInfoMap.put(globalId, clientInfo);
Luke Huang05298582021-06-13 16:52:05 +0000272 // Remove the cleanup event because here comes a new request.
273 cancelStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700274 }
275
276 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
Hugo Benichid2552ae2017-04-11 14:42:47 +0900277 clientInfo.mClientIds.delete(clientId);
278 clientInfo.mClientRequests.delete(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700279 mIdToClientInfoMap.remove(globalId);
Luke Huang05298582021-06-13 16:52:05 +0000280 maybeScheduleStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700281 }
282
Irfan Sheriff75006652012-04-17 23:15:29 -0700283 @Override
284 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900285 final ClientInfo clientInfo;
286 final int id;
287 final int clientId = msg.arg2;
288 final ListenerArgs args;
Irfan Sheriff75006652012-04-17 23:15:29 -0700289 switch (msg.what) {
Irfan Sheriff75006652012-04-17 23:15:29 -0700290 case NsdManager.DISABLE:
291 //TODO: cleanup clients
292 transitionTo(mDisabledState);
293 break;
294 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;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700400 case NsdManager.NATIVE_DAEMON_EVENT:
401 NativeEvent event = (NativeEvent) msg.obj;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700402 if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
Hugo Benichif0c84092017-04-05 14:43:29 +0900403 return NOT_HANDLED;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700404 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700405 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700406 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900407 return NOT_HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700408 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900409 return HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700410 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700411
412 private boolean handleNativeEvent(int code, String raw, String[] cooked) {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700413 NsdServiceInfo servInfo;
414 int id = Integer.parseInt(cooked[1]);
415 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
416 if (clientInfo == null) {
Hugo Benichi32be63d2017-04-05 14:06:11 +0900417 String name = NativeResponseCode.nameOf(code);
paulhub2225702021-11-17 09:35:33 +0800418 Log.e(TAG, String.format("id %d for %s has no client mapping", id, name));
Hugo Benichif0c84092017-04-05 14:43:29 +0900419 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700420 }
421
422 /* This goes in response as msg.arg2 */
Christopher Lane74411222014-04-25 18:39:07 -0700423 int clientId = clientInfo.getClientId(id);
424 if (clientId < 0) {
Vinit Deshapnde930a8512013-06-25 19:45:03 -0700425 // This can happen because of race conditions. For example,
426 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
427 // and we may get in this situation.
Hugo Benichi32be63d2017-04-05 14:06:11 +0900428 String name = NativeResponseCode.nameOf(code);
paulhub2225702021-11-17 09:35:33 +0800429 Log.d(TAG, String.format(
Hugo Benichi32be63d2017-04-05 14:06:11 +0900430 "Notification %s for listener id %d that is no longer active",
431 name, id));
Hugo Benichif0c84092017-04-05 14:43:29 +0900432 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700433 }
Hugo Benichi32be63d2017-04-05 14:06:11 +0900434 if (DBG) {
435 String name = NativeResponseCode.nameOf(code);
paulhub2225702021-11-17 09:35:33 +0800436 Log.d(TAG, String.format("Native daemon message %s: %s", name, raw));
Hugo Benichi32be63d2017-04-05 14:06:11 +0900437 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700438 switch (code) {
439 case NativeResponseCode.SERVICE_FOUND:
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900440 /* NNN uniqueId serviceName regType domain interfaceIdx netId */
Christopher Lane0f2cc362014-03-17 16:35:45 -0700441 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900442 final int foundNetId;
443 try {
444 foundNetId = Integer.parseInt(cooked[6]);
445 } catch (NumberFormatException e) {
446 Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]);
447 break;
448 }
449 if (foundNetId == 0L) {
450 // Ignore services that do not have a Network: they are not usable
451 // by apps, as they would need privileged permissions to use
452 // interfaces that do not have an associated Network.
453 break;
454 }
455 servInfo.setNetwork(new Network(foundNetId));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900456 clientInfo.onServiceFound(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700457 break;
458 case NativeResponseCode.SERVICE_LOST:
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900459 /* NNN uniqueId serviceName regType domain interfaceIdx netId */
460 final int lostNetId;
461 try {
462 lostNetId = Integer.parseInt(cooked[6]);
463 } catch (NumberFormatException e) {
464 Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]);
465 break;
466 }
Christopher Lane0f2cc362014-03-17 16:35:45 -0700467 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900468 // The network could be null if it was torn down when the service is lost
469 // TODO: avoid returning null in that case, possibly by remembering found
470 // services on the same interface index and their network at the time
471 servInfo.setNetwork(lostNetId == 0 ? null : new Network(lostNetId));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900472 clientInfo.onServiceLost(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700473 break;
474 case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
475 /* NNN uniqueId errorCode */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900476 clientInfo.onDiscoverServicesFailed(
477 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700478 break;
479 case NativeResponseCode.SERVICE_REGISTERED:
480 /* NNN regId serviceName regType */
Christopher Lane0f2cc362014-03-17 16:35:45 -0700481 servInfo = new NsdServiceInfo(cooked[2], null);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900482 clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700483 break;
484 case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
485 /* NNN regId errorCode */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900486 clientInfo.onRegisterServiceFailed(
487 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700488 break;
489 case NativeResponseCode.SERVICE_UPDATED:
490 /* NNN regId */
491 break;
492 case NativeResponseCode.SERVICE_UPDATE_FAILED:
493 /* NNN regId errorCode */
494 break;
495 case NativeResponseCode.SERVICE_RESOLVED:
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900496 /* NNN resolveId fullName hostName port txtlen txtdata interfaceIdx */
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700497 int index = 0;
498 while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
499 if (cooked[2].charAt(index) == '\\') {
500 ++index;
501 }
502 ++index;
503 }
504 if (index >= cooked[2].length()) {
paulhub2225702021-11-17 09:35:33 +0800505 Log.e(TAG, "Invalid service found " + raw);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700506 break;
507 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900508
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700509 String name = cooked[2].substring(0, index);
510 String rest = cooked[2].substring(index);
511 String type = rest.replace(".local.", "");
512
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700513 name = unescape(name);
514
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700515 clientInfo.mResolvedService.setServiceName(name);
516 clientInfo.mResolvedService.setServiceType(type);
517 clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
Philip P. Moltmannbbb41dd2016-03-16 10:15:39 -0700518 clientInfo.mResolvedService.setTxtRecords(cooked[6]);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900519 // Network will be added after SERVICE_GET_ADDR_SUCCESS
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700520
521 stopResolveService(id);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800522 removeRequestMap(clientId, id, clientInfo);
523
524 int id2 = getUniqueId();
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900525 if (getAddrInfo(id2, cooked[3], cooked[7] /* interfaceIdx */)) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800526 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800527 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900528 clientInfo.onResolveServiceFailed(
529 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700530 clientInfo.mResolvedService = null;
531 }
532 break;
533 case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
534 /* NNN resolveId errorCode */
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700535 stopResolveService(id);
536 removeRequestMap(clientId, id, clientInfo);
537 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900538 clientInfo.onResolveServiceFailed(
539 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700540 break;
541 case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
542 /* NNN resolveId errorCode */
543 stopGetAddrInfo(id);
544 removeRequestMap(clientId, id, clientInfo);
545 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900546 clientInfo.onResolveServiceFailed(
547 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700548 break;
549 case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900550 /* NNN resolveId hostname ttl addr interfaceIdx netId */
551 Network network = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700552 try {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900553 final int netId = Integer.parseInt(cooked[6]);
554 network = netId == 0L ? null : new Network(netId);
555 } catch (NumberFormatException e) {
556 Log.wtf(TAG, "Invalid network in GET_ADDR_SUCCESS: " + cooked[6], e);
557 }
558
559 InetAddress serviceHost = null;
560 try {
561 serviceHost = InetAddress.getByName(cooked[4]);
562 } catch (UnknownHostException e) {
563 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
564 }
565
566 // If the resolved service is on an interface without a network, consider it
567 // as a failure: it would not be usable by apps as they would need
568 // privileged permissions.
569 if (network != null && serviceHost != null) {
570 clientInfo.mResolvedService.setHost(serviceHost);
571 clientInfo.mResolvedService.setNetwork(network);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900572 clientInfo.onResolveServiceSucceeded(
573 clientId, clientInfo.mResolvedService);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900574 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900575 clientInfo.onResolveServiceFailed(
576 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700577 }
578 stopGetAddrInfo(id);
579 removeRequestMap(clientId, id, clientInfo);
580 clientInfo.mResolvedService = null;
581 break;
582 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900583 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700584 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900585 return true;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700586 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700587 }
588 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700589
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700590 private String unescape(String s) {
591 StringBuilder sb = new StringBuilder(s.length());
592 for (int i = 0; i < s.length(); ++i) {
593 char c = s.charAt(i);
594 if (c == '\\') {
595 if (++i >= s.length()) {
paulhub2225702021-11-17 09:35:33 +0800596 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700597 break;
598 }
599 c = s.charAt(i);
600 if (c != '.' && c != '\\') {
601 if (i + 2 >= s.length()) {
paulhub2225702021-11-17 09:35:33 +0800602 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700603 break;
604 }
605 c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
606 i += 2;
607 }
608 }
609 sb.append(c);
610 }
611 return sb.toString();
612 }
613
Hugo Benichi803a2f02017-04-24 11:35:06 +0900614 @VisibleForTesting
paulhu5568f452021-11-30 13:31:29 +0800615 NsdService(Context ctx, Handler handler, DaemonConnectionSupplier fn, long cleanupDelayMs) {
Luke Huang05298582021-06-13 16:52:05 +0000616 mCleanupDelayMs = cleanupDelayMs;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900617 mContext = ctx;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900618 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700619 mNsdStateMachine.start();
Hugo Benichi6d706442017-04-24 16:19:58 +0900620 mDaemonCallback = new NativeCallbackReceiver();
621 mDaemon = fn.get(mDaemonCallback);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700622 }
623
624 public static NsdService create(Context context) throws InterruptedException {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900625 HandlerThread thread = new HandlerThread(TAG);
626 thread.start();
627 Handler handler = new Handler(thread.getLooper());
paulhu5568f452021-11-30 13:31:29 +0800628 NsdService service =
629 new NsdService(context, handler, DaemonConnection::new, CLEANUP_DELAY_MS);
Hugo Benichi6d706442017-04-24 16:19:58 +0900630 service.mDaemonCallback.awaitConnection();
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700631 return service;
632 }
633
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900634 @Override
635 public INsdServiceConnector connect(INsdManagerCallback cb) {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900636 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900637 final INsdServiceConnector connector = new NsdServiceConnector();
638 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
639 NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb)));
640 return connector;
Irfan Sheriff75006652012-04-17 23:15:29 -0700641 }
642
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900643 private static class ListenerArgs {
644 public final NsdServiceConnector connector;
645 public final NsdServiceInfo serviceInfo;
646 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
647 this.connector = connector;
648 this.serviceInfo = serviceInfo;
649 }
650 }
651
652 private class NsdServiceConnector extends INsdServiceConnector.Stub
653 implements IBinder.DeathRecipient {
654 @Override
655 public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
656 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
657 NsdManager.REGISTER_SERVICE, 0, listenerKey,
658 new ListenerArgs(this, serviceInfo)));
659 }
660
661 @Override
662 public void unregisterService(int listenerKey) {
663 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
664 NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
665 new ListenerArgs(this, null)));
666 }
667
668 @Override
669 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
670 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
671 NsdManager.DISCOVER_SERVICES, 0, listenerKey,
672 new ListenerArgs(this, serviceInfo)));
673 }
674
675 @Override
676 public void stopDiscovery(int listenerKey) {
677 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
678 NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
679 }
680
681 @Override
682 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
683 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
684 NsdManager.RESOLVE_SERVICE, 0, listenerKey,
685 new ListenerArgs(this, serviceInfo)));
686 }
687
688 @Override
689 public void startDaemon() {
690 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
691 NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
692 }
693
694 @Override
695 public void binderDied() {
696 mNsdStateMachine.sendMessage(
697 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
698 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700699 }
700
Hugo Benichi912db992017-04-24 16:41:03 +0900701 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff52fc83a2012-04-19 10:26:34 -0700702 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff75006652012-04-17 23:15:29 -0700703 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi912db992017-04-24 16:41:03 +0900704 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
705 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn692107e2012-08-29 18:32:08 -0700706 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff75006652012-04-17 23:15:29 -0700707 }
708
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700709 private int getUniqueId() {
710 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
711 return mUniqueId;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700712 }
713
Sreeram Ramachandranfba15592014-07-19 23:21:46 -0700714 /* These should be in sync with system/netd/server/ResponseCode.h */
Hugo Benichi32be63d2017-04-05 14:06:11 +0900715 static final class NativeResponseCode {
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700716 public static final int SERVICE_DISCOVERY_FAILED = 602;
717 public static final int SERVICE_FOUND = 603;
718 public static final int SERVICE_LOST = 604;
719
720 public static final int SERVICE_REGISTRATION_FAILED = 605;
721 public static final int SERVICE_REGISTERED = 606;
722
723 public static final int SERVICE_RESOLUTION_FAILED = 607;
724 public static final int SERVICE_RESOLVED = 608;
725
726 public static final int SERVICE_UPDATED = 609;
727 public static final int SERVICE_UPDATE_FAILED = 610;
728
729 public static final int SERVICE_GET_ADDR_FAILED = 611;
730 public static final int SERVICE_GET_ADDR_SUCCESS = 612;
Hugo Benichi32be63d2017-04-05 14:06:11 +0900731
732 private static final SparseArray<String> CODE_NAMES = new SparseArray<>();
733 static {
734 CODE_NAMES.put(SERVICE_DISCOVERY_FAILED, "SERVICE_DISCOVERY_FAILED");
735 CODE_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND");
736 CODE_NAMES.put(SERVICE_LOST, "SERVICE_LOST");
737 CODE_NAMES.put(SERVICE_REGISTRATION_FAILED, "SERVICE_REGISTRATION_FAILED");
738 CODE_NAMES.put(SERVICE_REGISTERED, "SERVICE_REGISTERED");
739 CODE_NAMES.put(SERVICE_RESOLUTION_FAILED, "SERVICE_RESOLUTION_FAILED");
740 CODE_NAMES.put(SERVICE_RESOLVED, "SERVICE_RESOLVED");
741 CODE_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
742 CODE_NAMES.put(SERVICE_UPDATE_FAILED, "SERVICE_UPDATE_FAILED");
743 CODE_NAMES.put(SERVICE_GET_ADDR_FAILED, "SERVICE_GET_ADDR_FAILED");
744 CODE_NAMES.put(SERVICE_GET_ADDR_SUCCESS, "SERVICE_GET_ADDR_SUCCESS");
745 }
746
747 static String nameOf(int code) {
748 String name = CODE_NAMES.get(code);
749 if (name == null) {
750 return Integer.toString(code);
751 }
752 return name;
753 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700754 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700755
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700756 private class NativeEvent {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700757 final int code;
758 final String raw;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700759 final String[] cooked;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700760
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700761 NativeEvent(int code, String raw, String[] cooked) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700762 this.code = code;
763 this.raw = raw;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700764 this.cooked = cooked;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700765 }
766 }
767
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700768 class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
Hugo Benichi6d706442017-04-24 16:19:58 +0900769 private final CountDownLatch connected = new CountDownLatch(1);
770
771 public void awaitConnection() throws InterruptedException {
772 connected.await();
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700773 }
774
Hugo Benichi6d706442017-04-24 16:19:58 +0900775 @Override
776 public void onDaemonConnected() {
777 connected.countDown();
778 }
779
780 @Override
Dianne Hackborn9cd0fea2014-02-26 16:20:52 -0800781 public boolean onCheckHoldWakeLock(int code) {
782 return false;
783 }
784
Hugo Benichi6d706442017-04-24 16:19:58 +0900785 @Override
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700786 public boolean onEvent(int code, String raw, String[] cooked) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700787 // TODO: NDC translates a message to a callback, we could enhance NDC to
788 // directly interact with a state machine through messages
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700789 NativeEvent event = new NativeEvent(code, raw, cooked);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700790 mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
791 return true;
792 }
793 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700794
Hugo Benichi6d706442017-04-24 16:19:58 +0900795 interface DaemonConnectionSupplier {
796 DaemonConnection get(NativeCallbackReceiver callback);
797 }
798
799 @VisibleForTesting
800 public static class DaemonConnection {
801 final NativeDaemonConnector mNativeConnector;
Luke Huang05298582021-06-13 16:52:05 +0000802 boolean mIsStarted = false;
Hugo Benichi6d706442017-04-24 16:19:58 +0900803
804 DaemonConnection(NativeCallbackReceiver callback) {
805 mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
806 new Thread(mNativeConnector, MDNS_TAG).start();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700807 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900808
Luke Huang05298582021-06-13 16:52:05 +0000809 /**
810 * Executes the specified cmd on the daemon.
811 */
Hugo Benichi6d706442017-04-24 16:19:58 +0900812 public boolean execute(Object... args) {
813 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800814 Log.d(TAG, "mdnssd " + Arrays.toString(args));
Hugo Benichi6d706442017-04-24 16:19:58 +0900815 }
816 try {
817 mNativeConnector.execute("mdnssd", args);
818 } catch (NativeDaemonConnectorException e) {
paulhub2225702021-11-17 09:35:33 +0800819 Log.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
Hugo Benichi6d706442017-04-24 16:19:58 +0900820 return false;
821 }
822 return true;
823 }
824
Luke Huang05298582021-06-13 16:52:05 +0000825 /**
826 * Starts the daemon if it is not already started.
827 */
828 public void maybeStart() {
829 if (mIsStarted) {
830 return;
831 }
Hugo Benichi4dd4db72017-04-28 15:31:10 +0900832 execute("start-service");
Luke Huang05298582021-06-13 16:52:05 +0000833 mIsStarted = true;
Hugo Benichi6d706442017-04-24 16:19:58 +0900834 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900835
Luke Huang05298582021-06-13 16:52:05 +0000836 /**
837 * Stops the daemon if it is started.
838 */
839 public void maybeStop() {
840 if (!mIsStarted) {
841 return;
842 }
Hugo Benichi4dd4db72017-04-28 15:31:10 +0900843 execute("stop-service");
Luke Huang05298582021-06-13 16:52:05 +0000844 mIsStarted = false;
Hugo Benichi4dd4db72017-04-28 15:31:10 +0900845 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700846 }
847
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700848 private boolean registerService(int regId, NsdServiceInfo service) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900849 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800850 Log.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700851 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900852 String name = service.getServiceName();
853 String type = service.getServiceType();
854 int port = service.getPort();
855 byte[] textRecord = service.getTxtRecord();
856 String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
Hugo Benichi4dd4db72017-04-28 15:31:10 +0900857 return mDaemon.execute("register", regId, name, type, port, record);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700858 }
859
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700860 private boolean unregisterService(int regId) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900861 return mDaemon.execute("stop-register", regId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700862 }
863
864 private boolean updateService(int regId, DnsSdTxtRecord t) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900865 if (t == null) {
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700866 return false;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700867 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900868 return mDaemon.execute("update", regId, t.size(), t.getRawData());
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700869 }
870
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900871 private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
872 final Network network = serviceInfo.getNetwork();
873 final int discoverInterface = getNetworkInterfaceIndex(network);
874 if (network != null && discoverInterface == IFACE_IDX_ANY) {
875 Log.e(TAG, "Interface to discover service on not found");
876 return false;
877 }
878 return mDaemon.execute("discover", discoveryId, serviceInfo.getServiceType(),
879 discoverInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700880 }
881
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700882 private boolean stopServiceDiscovery(int discoveryId) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900883 return mDaemon.execute("stop-discover", discoveryId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700884 }
885
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700886 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900887 final String name = service.getServiceName();
888 final String type = service.getServiceType();
889 final Network network = service.getNetwork();
890 final int resolveInterface = getNetworkInterfaceIndex(network);
891 if (network != null && resolveInterface == IFACE_IDX_ANY) {
892 Log.e(TAG, "Interface to resolve service on not found");
893 return false;
894 }
895 return mDaemon.execute("resolve", resolveId, name, type, "local.", resolveInterface);
896 }
897
898 /**
899 * Guess the interface to use to resolve or discover a service on a specific network.
900 *
901 * This is an imperfect guess, as for example the network may be gone or not yet fully
902 * registered. This is fine as failing is correct if the network is gone, and a client
903 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
904 * this is to support the legacy mdnsresponder implementation, which historically resolved
905 * services on an unspecified network.
906 */
907 private int getNetworkInterfaceIndex(Network network) {
908 if (network == null) return IFACE_IDX_ANY;
909
910 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
911 if (cm == null) {
912 Log.wtf(TAG, "No ConnectivityManager for resolveService");
913 return IFACE_IDX_ANY;
914 }
915 final LinkProperties lp = cm.getLinkProperties(network);
916 if (lp == null) return IFACE_IDX_ANY;
917
918 // Only resolve on non-stacked interfaces
919 final NetworkInterface iface;
920 try {
921 iface = NetworkInterface.getByName(lp.getInterfaceName());
922 } catch (SocketException e) {
923 Log.e(TAG, "Error querying interface", e);
924 return IFACE_IDX_ANY;
925 }
926
927 if (iface == null) {
928 Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
929 return IFACE_IDX_ANY;
930 }
931
932 return iface.getIndex();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700933 }
934
935 private boolean stopResolveService(int resolveId) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900936 return mDaemon.execute("stop-resolve", resolveId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700937 }
938
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900939 private boolean getAddrInfo(int resolveId, String hostname, String interfaceIdx) {
940 // interfaceIdx is always obtained (as string) from the service resolved callback
941 return mDaemon.execute("getaddrinfo", resolveId, hostname, interfaceIdx);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700942 }
943
944 private boolean stopGetAddrInfo(int resolveId) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900945 return mDaemon.execute("stop-getaddrinfo", resolveId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700946 }
947
948 @Override
Irfan Sheriff75006652012-04-17 23:15:29 -0700949 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
paulhub2225702021-11-17 09:35:33 +0800950 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
951 != PackageManager.PERMISSION_GRANTED) {
952 pw.println("Permission Denial: can't dump " + TAG
953 + " due to missing android.permission.DUMP permission");
954 return;
955 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700956
Irfan Sheriff75006652012-04-17 23:15:29 -0700957 for (ClientInfo client : mClients.values()) {
958 pw.println("Client Info");
959 pw.println(client);
960 }
961
962 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700963 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700964
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700965 /* Information tracked per client */
966 private class ClientInfo {
967
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700968 private static final int MAX_LIMIT = 10;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900969 private final INsdManagerCallback mCb;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700970 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700971 private NsdServiceInfo mResolvedService;
972
973 /* A map from client id to unique id sent to mDns */
Hugo Benichid2552ae2017-04-11 14:42:47 +0900974 private final SparseIntArray mClientIds = new SparseIntArray();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700975
Dave Plattfeff2af2014-03-07 14:48:22 -0800976 /* A map from client id to the type of the request we had received */
Hugo Benichid2552ae2017-04-11 14:42:47 +0900977 private final SparseIntArray mClientRequests = new SparseIntArray();
Dave Plattfeff2af2014-03-07 14:48:22 -0800978
Luke Huangf7277ed2021-07-12 21:15:10 +0800979 // The target SDK of this client < Build.VERSION_CODES.S
980 private boolean mIsLegacy = false;
981
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900982 private ClientInfo(INsdManagerCallback cb) {
983 mCb = cb;
paulhub2225702021-11-17 09:35:33 +0800984 if (DBG) Log.d(TAG, "New client");
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700985 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700986
987 @Override
988 public String toString() {
Jeff Sharkey63465382020-10-17 21:20:13 -0600989 StringBuilder sb = new StringBuilder();
Irfan Sheriff75006652012-04-17 23:15:29 -0700990 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Luke Huangf7277ed2021-07-12 21:15:10 +0800991 sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700992 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800993 int clientID = mClientIds.keyAt(i);
994 sb.append("clientId ").append(clientID).
995 append(" mDnsId ").append(mClientIds.valueAt(i)).
996 append(" type ").append(mClientRequests.get(clientID)).append("\n");
Irfan Sheriff75006652012-04-17 23:15:29 -0700997 }
998 return sb.toString();
999 }
Dave Plattfeff2af2014-03-07 14:48:22 -08001000
Luke Huangf7277ed2021-07-12 21:15:10 +08001001 private boolean isLegacy() {
1002 return mIsLegacy;
1003 }
1004
1005 private void setLegacy() {
1006 mIsLegacy = true;
1007 }
1008
Dave Plattfeff2af2014-03-07 14:48:22 -08001009 // Remove any pending requests from the global map when we get rid of a client,
1010 // and send cancellations to the daemon.
1011 private void expungeAllRequests() {
1012 int globalId, clientId, i;
Hugo Benichid2552ae2017-04-11 14:42:47 +09001013 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Dave Plattfeff2af2014-03-07 14:48:22 -08001014 for (i = 0; i < mClientIds.size(); i++) {
1015 clientId = mClientIds.keyAt(i);
1016 globalId = mClientIds.valueAt(i);
1017 mIdToClientInfoMap.remove(globalId);
paulhub2225702021-11-17 09:35:33 +08001018 if (DBG) {
1019 Log.d(TAG, "Terminating client-ID " + clientId
1020 + " global-ID " + globalId + " type " + mClientRequests.get(clientId));
1021 }
Dave Plattfeff2af2014-03-07 14:48:22 -08001022 switch (mClientRequests.get(clientId)) {
1023 case NsdManager.DISCOVER_SERVICES:
1024 stopServiceDiscovery(globalId);
1025 break;
1026 case NsdManager.RESOLVE_SERVICE:
1027 stopResolveService(globalId);
1028 break;
1029 case NsdManager.REGISTER_SERVICE:
1030 unregisterService(globalId);
1031 break;
1032 default:
1033 break;
1034 }
1035 }
1036 mClientIds.clear();
1037 mClientRequests.clear();
1038 }
1039
Christopher Lane74411222014-04-25 18:39:07 -07001040 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
1041 // return the corresponding listener id. mDnsClient id is also called a global id.
1042 private int getClientId(final int globalId) {
Hugo Benichid2552ae2017-04-11 14:42:47 +09001043 int idx = mClientIds.indexOfValue(globalId);
1044 if (idx < 0) {
1045 return idx;
Christopher Lane74411222014-04-25 18:39:07 -07001046 }
Hugo Benichid2552ae2017-04-11 14:42:47 +09001047 return mClientIds.keyAt(idx);
Christopher Lane74411222014-04-25 18:39:07 -07001048 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001049
1050 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
1051 try {
1052 mCb.onDiscoverServicesStarted(listenerKey, info);
1053 } catch (RemoteException e) {
1054 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
1055 }
1056 }
1057
1058 void onDiscoverServicesFailed(int listenerKey, int error) {
1059 try {
1060 mCb.onDiscoverServicesFailed(listenerKey, error);
1061 } catch (RemoteException e) {
1062 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
1063 }
1064 }
1065
1066 void onServiceFound(int listenerKey, NsdServiceInfo info) {
1067 try {
1068 mCb.onServiceFound(listenerKey, info);
1069 } catch (RemoteException e) {
1070 Log.e(TAG, "Error calling onServiceFound(", e);
1071 }
1072 }
1073
1074 void onServiceLost(int listenerKey, NsdServiceInfo info) {
1075 try {
1076 mCb.onServiceLost(listenerKey, info);
1077 } catch (RemoteException e) {
1078 Log.e(TAG, "Error calling onServiceLost(", e);
1079 }
1080 }
1081
1082 void onStopDiscoveryFailed(int listenerKey, int error) {
1083 try {
1084 mCb.onStopDiscoveryFailed(listenerKey, error);
1085 } catch (RemoteException e) {
1086 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
1087 }
1088 }
1089
1090 void onStopDiscoverySucceeded(int listenerKey) {
1091 try {
1092 mCb.onStopDiscoverySucceeded(listenerKey);
1093 } catch (RemoteException e) {
1094 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
1095 }
1096 }
1097
1098 void onRegisterServiceFailed(int listenerKey, int error) {
1099 try {
1100 mCb.onRegisterServiceFailed(listenerKey, error);
1101 } catch (RemoteException e) {
1102 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
1103 }
1104 }
1105
1106 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1107 try {
1108 mCb.onRegisterServiceSucceeded(listenerKey, info);
1109 } catch (RemoteException e) {
1110 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
1111 }
1112 }
1113
1114 void onUnregisterServiceFailed(int listenerKey, int error) {
1115 try {
1116 mCb.onUnregisterServiceFailed(listenerKey, error);
1117 } catch (RemoteException e) {
1118 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
1119 }
1120 }
1121
1122 void onUnregisterServiceSucceeded(int listenerKey) {
1123 try {
1124 mCb.onUnregisterServiceSucceeded(listenerKey);
1125 } catch (RemoteException e) {
1126 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
1127 }
1128 }
1129
1130 void onResolveServiceFailed(int listenerKey, int error) {
1131 try {
1132 mCb.onResolveServiceFailed(listenerKey, error);
1133 } catch (RemoteException e) {
1134 Log.e(TAG, "Error calling onResolveServiceFailed", e);
1135 }
1136 }
1137
1138 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1139 try {
1140 mCb.onResolveServiceSucceeded(listenerKey, info);
1141 } catch (RemoteException e) {
1142 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
1143 }
1144 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001145 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001146}