blob: 497107dbf9896e2e846b2ce61a2f6b58acbb0962 [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;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070022import android.net.nsd.INsdManager;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090023import android.net.nsd.INsdManagerCallback;
24import android.net.nsd.INsdServiceConnector;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070025import android.net.nsd.NsdManager;
paulhua262cc12019-08-12 16:25:11 +080026import android.net.nsd.NsdServiceInfo;
Hugo Benichi803a2f02017-04-24 11:35:06 +090027import android.os.Handler;
paulhua262cc12019-08-12 16:25:11 +080028import android.os.HandlerThread;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090029import android.os.IBinder;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070030import android.os.Message;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090031import android.os.RemoteException;
Dianne Hackborn692107e2012-08-29 18:32:08 -070032import android.os.UserHandle;
Philip P. Moltmannbbb41dd2016-03-16 10:15:39 -070033import android.util.Base64;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090034import android.util.Log;
35import android.util.Pair;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070036import android.util.SparseArray;
Hugo Benichid2552ae2017-04-11 14:42:47 +090037import android.util.SparseIntArray;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070038
paulhua262cc12019-08-12 16:25:11 +080039import com.android.internal.annotations.VisibleForTesting;
paulhua262cc12019-08-12 16:25:11 +080040import com.android.internal.util.State;
41import com.android.internal.util.StateMachine;
Chalard Jean339c0102020-08-19 16:07:22 +090042import com.android.net.module.util.DnsSdTxtRecord;
paulhua262cc12019-08-12 16:25:11 +080043
Irfan Sheriff77ec5582012-03-22 17:01:39 -070044import java.io.FileDescriptor;
45import java.io.PrintWriter;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070046import java.net.InetAddress;
Hugo Benichi6d706442017-04-24 16:19:58 +090047import java.util.Arrays;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070048import java.util.HashMap;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070049import java.util.concurrent.CountDownLatch;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070050
Irfan Sheriff77ec5582012-03-22 17:01:39 -070051/**
52 * Network Service Discovery Service handles remote service discovery operation requests by
53 * implementing the INsdManager interface.
54 *
55 * @hide
56 */
57public class NsdService extends INsdManager.Stub {
58 private static final String TAG = "NsdService";
59 private static final String MDNS_TAG = "mDnsConnector";
60
Hugo Benichi32be63d2017-04-05 14:06:11 +090061 private static final boolean DBG = true;
Luke Huang92860f92021-06-23 06:29:30 +000062 private static final long CLEANUP_DELAY_MS = 10000;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070063
Hugo Benichi32be63d2017-04-05 14:06:11 +090064 private final Context mContext;
Hugo Benichi32be63d2017-04-05 14:06:11 +090065 private final NsdStateMachine mNsdStateMachine;
Hugo Benichi6d706442017-04-24 16:19:58 +090066 private final DaemonConnection mDaemon;
67 private final NativeCallbackReceiver mDaemonCallback;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070068
69 /**
70 * Clients receiving asynchronous messages
71 */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090072 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff77ec5582012-03-22 17:01:39 -070073
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070074 /* A map from unique id to client info */
Hugo Benichi32be63d2017-04-05 14:06:11 +090075 private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070076
Luke Huang05298582021-06-13 16:52:05 +000077 private final long mCleanupDelayMs;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070078
Hugo Benichi32be63d2017-04-05 14:06:11 +090079 private static final int INVALID_ID = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070080 private int mUniqueId = 1;
Luke Huangf7277ed2021-07-12 21:15:10 +080081 // The count of the connected legacy clients.
82 private int mLegacyClientCount = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070083
Irfan Sheriff75006652012-04-17 23:15:29 -070084 private class NsdStateMachine extends StateMachine {
85
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070086 private final DefaultState mDefaultState = new DefaultState();
87 private final DisabledState mDisabledState = new DisabledState();
88 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff75006652012-04-17 23:15:29 -070089
90 @Override
Wink Saville358f5d42012-05-29 12:40:46 -070091 protected String getWhatToString(int what) {
Hugo Benichi32be63d2017-04-05 14:06:11 +090092 return NsdManager.nameOf(what);
Irfan Sheriff75006652012-04-17 23:15:29 -070093 }
94
Luke Huang92860f92021-06-23 06:29:30 +000095 private void maybeStartDaemon() {
Luke Huang05298582021-06-13 16:52:05 +000096 mDaemon.maybeStart();
97 maybeScheduleStop();
98 }
99
Luke Huang92860f92021-06-23 06:29:30 +0000100 private boolean isAnyRequestActive() {
101 return mIdToClientInfoMap.size() != 0;
102 }
103
104 private void scheduleStop() {
105 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
106 }
107 private void maybeScheduleStop() {
Luke Huangf7277ed2021-07-12 21:15:10 +0800108 // The native daemon should stay alive and can't be cleanup
109 // if any legacy client connected.
110 if (!isAnyRequestActive() && mLegacyClientCount == 0) {
Luke Huang92860f92021-06-23 06:29:30 +0000111 scheduleStop();
Luke Huang05298582021-06-13 16:52:05 +0000112 }
113 }
114
Luke Huang92860f92021-06-23 06:29:30 +0000115 private void cancelStop() {
Luke Huang05298582021-06-13 16:52:05 +0000116 this.removeMessages(NsdManager.DAEMON_CLEANUP);
117 }
118
Hugo Benichi803a2f02017-04-24 11:35:06 +0900119 NsdStateMachine(String name, Handler handler) {
120 super(name, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700121 addState(mDefaultState);
122 addState(mDisabledState, mDefaultState);
123 addState(mEnabledState, mDefaultState);
paulhu5568f452021-11-30 13:31:29 +0800124 State initialState = mEnabledState;
Hugo Benichi912db992017-04-24 16:41:03 +0900125 setInitialState(initialState);
Wink Saville358f5d42012-05-29 12:40:46 -0700126 setLogRecSize(25);
Irfan Sheriff75006652012-04-17 23:15:29 -0700127 }
128
129 class DefaultState extends State {
130 @Override
131 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900132 final ClientInfo cInfo;
133 final int clientId = msg.arg2;
Irfan Sheriff75006652012-04-17 23:15:29 -0700134 switch (msg.what) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900135 case NsdManager.REGISTER_CLIENT:
136 final Pair<NsdServiceConnector, INsdManagerCallback> arg =
137 (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj;
138 final INsdManagerCallback cb = arg.second;
139 try {
140 cb.asBinder().linkToDeath(arg.first, 0);
141 cInfo = new ClientInfo(cb);
142 mClients.put(arg.first, cInfo);
143 } catch (RemoteException e) {
144 Log.w(TAG, "Client " + clientId + " has already died");
Irfan Sheriff75006652012-04-17 23:15:29 -0700145 }
146 break;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900147 case NsdManager.UNREGISTER_CLIENT:
148 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
149 cInfo = mClients.remove(connector);
Dave Plattfeff2af2014-03-07 14:48:22 -0800150 if (cInfo != null) {
151 cInfo.expungeAllRequests();
Luke Huangf7277ed2021-07-12 21:15:10 +0800152 if (cInfo.isLegacy()) {
153 mLegacyClientCount -= 1;
154 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800155 }
Luke Huangf7277ed2021-07-12 21:15:10 +0800156 maybeScheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700157 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700158 case NsdManager.DISCOVER_SERVICES:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900159 cInfo = getClientInfoForReply(msg);
160 if (cInfo != null) {
161 cInfo.onDiscoverServicesFailed(
162 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
163 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700164 break;
165 case NsdManager.STOP_DISCOVERY:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900166 cInfo = getClientInfoForReply(msg);
167 if (cInfo != null) {
168 cInfo.onStopDiscoveryFailed(
169 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
170 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700171 break;
172 case NsdManager.REGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900173 cInfo = getClientInfoForReply(msg);
174 if (cInfo != null) {
175 cInfo.onRegisterServiceFailed(
176 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
177 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700178 break;
179 case NsdManager.UNREGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900180 cInfo = getClientInfoForReply(msg);
181 if (cInfo != null) {
182 cInfo.onUnregisterServiceFailed(
183 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
184 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700185 break;
186 case NsdManager.RESOLVE_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900187 cInfo = getClientInfoForReply(msg);
188 if (cInfo != null) {
189 cInfo.onResolveServiceFailed(
190 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
191 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700192 break;
Luke Huang05298582021-06-13 16:52:05 +0000193 case NsdManager.DAEMON_CLEANUP:
194 mDaemon.maybeStop();
195 break;
Luke Huangf7277ed2021-07-12 21:15:10 +0800196 // This event should be only sent by the legacy (target SDK < S) clients.
197 // Mark the sending client as legacy.
198 case NsdManager.DAEMON_STARTUP:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900199 cInfo = getClientInfoForReply(msg);
Luke Huangf7277ed2021-07-12 21:15:10 +0800200 if (cInfo != null) {
201 cancelStop();
202 cInfo.setLegacy();
203 mLegacyClientCount += 1;
204 maybeStartDaemon();
205 }
206 break;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700207 case NsdManager.NATIVE_DAEMON_EVENT:
Irfan Sheriff75006652012-04-17 23:15:29 -0700208 default:
paulhub2225702021-11-17 09:35:33 +0800209 Log.e(TAG, "Unhandled " + msg);
Irfan Sheriff75006652012-04-17 23:15:29 -0700210 return NOT_HANDLED;
211 }
212 return HANDLED;
213 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900214
215 private ClientInfo getClientInfoForReply(Message msg) {
216 final ListenerArgs args = (ListenerArgs) msg.obj;
217 return mClients.get(args.connector);
218 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700219 }
220
221 class DisabledState extends State {
222 @Override
223 public void enter() {
224 sendNsdStateChangeBroadcast(false);
225 }
226
227 @Override
228 public boolean processMessage(Message msg) {
229 switch (msg.what) {
230 case NsdManager.ENABLE:
231 transitionTo(mEnabledState);
232 break;
233 default:
234 return NOT_HANDLED;
235 }
236 return HANDLED;
237 }
238 }
239
240 class EnabledState extends State {
241 @Override
242 public void enter() {
243 sendNsdStateChangeBroadcast(true);
Irfan Sheriff75006652012-04-17 23:15:29 -0700244 }
245
246 @Override
247 public void exit() {
Luke Huang05298582021-06-13 16:52:05 +0000248 // TODO: it is incorrect to stop the daemon without expunging all requests
249 // and sending error callbacks to clients.
Luke Huang92860f92021-06-23 06:29:30 +0000250 scheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700251 }
252
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700253 private boolean requestLimitReached(ClientInfo clientInfo) {
254 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
paulhub2225702021-11-17 09:35:33 +0800255 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700256 return true;
257 }
258 return false;
259 }
260
Dave Plattfeff2af2014-03-07 14:48:22 -0800261 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700262 clientInfo.mClientIds.put(clientId, globalId);
Dave Plattfeff2af2014-03-07 14:48:22 -0800263 clientInfo.mClientRequests.put(clientId, what);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700264 mIdToClientInfoMap.put(globalId, clientInfo);
Luke Huang05298582021-06-13 16:52:05 +0000265 // Remove the cleanup event because here comes a new request.
266 cancelStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700267 }
268
269 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
Hugo Benichid2552ae2017-04-11 14:42:47 +0900270 clientInfo.mClientIds.delete(clientId);
271 clientInfo.mClientRequests.delete(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700272 mIdToClientInfoMap.remove(globalId);
Luke Huang05298582021-06-13 16:52:05 +0000273 maybeScheduleStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700274 }
275
Irfan Sheriff75006652012-04-17 23:15:29 -0700276 @Override
277 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900278 final ClientInfo clientInfo;
279 final int id;
280 final int clientId = msg.arg2;
281 final ListenerArgs args;
Irfan Sheriff75006652012-04-17 23:15:29 -0700282 switch (msg.what) {
Irfan Sheriff75006652012-04-17 23:15:29 -0700283 case NsdManager.DISABLE:
284 //TODO: cleanup clients
285 transitionTo(mDisabledState);
286 break;
287 case NsdManager.DISCOVER_SERVICES:
paulhub2225702021-11-17 09:35:33 +0800288 if (DBG) Log.d(TAG, "Discover services");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900289 args = (ListenerArgs) msg.obj;
290 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700291
292 if (requestLimitReached(clientInfo)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900293 clientInfo.onDiscoverServicesFailed(
294 clientId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff75006652012-04-17 23:15:29 -0700295 break;
296 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700297
Luke Huang05298582021-06-13 16:52:05 +0000298 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700299 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900300 if (discoverServices(id, args.serviceInfo.getServiceType())) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700301 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800302 Log.d(TAG, "Discover " + msg.arg2 + " " + id
303 + args.serviceInfo.getServiceType());
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700304 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900305 storeRequestMap(clientId, id, clientInfo, msg.what);
306 clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo);
Irfan Sheriff75006652012-04-17 23:15:29 -0700307 } else {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700308 stopServiceDiscovery(id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900309 clientInfo.onDiscoverServicesFailed(clientId,
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700310 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700311 }
312 break;
313 case NsdManager.STOP_DISCOVERY:
paulhub2225702021-11-17 09:35:33 +0800314 if (DBG) Log.d(TAG, "Stop service discovery");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900315 args = (ListenerArgs) msg.obj;
316 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700317
318 try {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900319 id = clientInfo.mClientIds.get(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700320 } catch (NullPointerException e) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900321 clientInfo.onStopDiscoveryFailed(
322 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700323 break;
324 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900325 removeRequestMap(clientId, id, clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700326 if (stopServiceDiscovery(id)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900327 clientInfo.onStopDiscoverySucceeded(clientId);
Irfan Sheriff75006652012-04-17 23:15:29 -0700328 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900329 clientInfo.onStopDiscoveryFailed(
330 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700331 }
332 break;
333 case NsdManager.REGISTER_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800334 if (DBG) Log.d(TAG, "Register service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900335 args = (ListenerArgs) msg.obj;
336 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700337 if (requestLimitReached(clientInfo)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900338 clientInfo.onRegisterServiceFailed(
339 clientId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700340 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700341 }
342
Luke Huang05298582021-06-13 16:52:05 +0000343 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700344 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900345 if (registerService(id, args.serviceInfo)) {
paulhub2225702021-11-17 09:35:33 +0800346 if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900347 storeRequestMap(clientId, id, clientInfo, msg.what);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700348 // Return success after mDns reports success
Irfan Sheriff75006652012-04-17 23:15:29 -0700349 } else {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700350 unregisterService(id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900351 clientInfo.onRegisterServiceFailed(
352 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700353 }
354 break;
355 case NsdManager.UNREGISTER_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800356 if (DBG) Log.d(TAG, "unregister service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900357 args = (ListenerArgs) msg.obj;
358 clientInfo = mClients.get(args.connector);
359 if (clientInfo == null) {
paulhub2225702021-11-17 09:35:33 +0800360 Log.e(TAG, "Unknown connector in unregistration");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700361 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700362 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900363 id = clientInfo.mClientIds.get(clientId);
364 removeRequestMap(clientId, id, clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700365 if (unregisterService(id)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900366 clientInfo.onUnregisterServiceSucceeded(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700367 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900368 clientInfo.onUnregisterServiceFailed(
369 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700370 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700371 break;
372 case NsdManager.RESOLVE_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800373 if (DBG) Log.d(TAG, "Resolve service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900374 args = (ListenerArgs) msg.obj;
375 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700376
377 if (clientInfo.mResolvedService != null) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900378 clientInfo.onResolveServiceFailed(
379 clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
Irfan Sheriff75006652012-04-17 23:15:29 -0700380 break;
381 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700382
Luke Huang05298582021-06-13 16:52:05 +0000383 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700384 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900385 if (resolveService(id, args.serviceInfo)) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700386 clientInfo.mResolvedService = new NsdServiceInfo();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900387 storeRequestMap(clientId, id, clientInfo, msg.what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700388 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900389 clientInfo.onResolveServiceFailed(
390 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700391 }
392 break;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700393 case NsdManager.NATIVE_DAEMON_EVENT:
394 NativeEvent event = (NativeEvent) msg.obj;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700395 if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
Hugo Benichif0c84092017-04-05 14:43:29 +0900396 return NOT_HANDLED;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700397 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700398 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700399 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900400 return NOT_HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700401 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900402 return HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700403 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700404
405 private boolean handleNativeEvent(int code, String raw, String[] cooked) {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700406 NsdServiceInfo servInfo;
407 int id = Integer.parseInt(cooked[1]);
408 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
409 if (clientInfo == null) {
Hugo Benichi32be63d2017-04-05 14:06:11 +0900410 String name = NativeResponseCode.nameOf(code);
paulhub2225702021-11-17 09:35:33 +0800411 Log.e(TAG, String.format("id %d for %s has no client mapping", id, name));
Hugo Benichif0c84092017-04-05 14:43:29 +0900412 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700413 }
414
415 /* This goes in response as msg.arg2 */
Christopher Lane74411222014-04-25 18:39:07 -0700416 int clientId = clientInfo.getClientId(id);
417 if (clientId < 0) {
Vinit Deshapnde930a8512013-06-25 19:45:03 -0700418 // This can happen because of race conditions. For example,
419 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
420 // and we may get in this situation.
Hugo Benichi32be63d2017-04-05 14:06:11 +0900421 String name = NativeResponseCode.nameOf(code);
paulhub2225702021-11-17 09:35:33 +0800422 Log.d(TAG, String.format(
Hugo Benichi32be63d2017-04-05 14:06:11 +0900423 "Notification %s for listener id %d that is no longer active",
424 name, id));
Hugo Benichif0c84092017-04-05 14:43:29 +0900425 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700426 }
Hugo Benichi32be63d2017-04-05 14:06:11 +0900427 if (DBG) {
428 String name = NativeResponseCode.nameOf(code);
paulhub2225702021-11-17 09:35:33 +0800429 Log.d(TAG, String.format("Native daemon message %s: %s", name, raw));
Hugo Benichi32be63d2017-04-05 14:06:11 +0900430 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700431 switch (code) {
432 case NativeResponseCode.SERVICE_FOUND:
433 /* NNN uniqueId serviceName regType domain */
Christopher Lane0f2cc362014-03-17 16:35:45 -0700434 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900435 clientInfo.onServiceFound(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700436 break;
437 case NativeResponseCode.SERVICE_LOST:
438 /* NNN uniqueId serviceName regType domain */
Christopher Lane0f2cc362014-03-17 16:35:45 -0700439 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900440 clientInfo.onServiceLost(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700441 break;
442 case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
443 /* NNN uniqueId errorCode */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900444 clientInfo.onDiscoverServicesFailed(
445 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700446 break;
447 case NativeResponseCode.SERVICE_REGISTERED:
448 /* NNN regId serviceName regType */
Christopher Lane0f2cc362014-03-17 16:35:45 -0700449 servInfo = new NsdServiceInfo(cooked[2], null);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900450 clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700451 break;
452 case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
453 /* NNN regId errorCode */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900454 clientInfo.onRegisterServiceFailed(
455 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700456 break;
457 case NativeResponseCode.SERVICE_UPDATED:
458 /* NNN regId */
459 break;
460 case NativeResponseCode.SERVICE_UPDATE_FAILED:
461 /* NNN regId errorCode */
462 break;
463 case NativeResponseCode.SERVICE_RESOLVED:
464 /* NNN resolveId fullName hostName port txtlen txtdata */
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700465 int index = 0;
466 while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
467 if (cooked[2].charAt(index) == '\\') {
468 ++index;
469 }
470 ++index;
471 }
472 if (index >= cooked[2].length()) {
paulhub2225702021-11-17 09:35:33 +0800473 Log.e(TAG, "Invalid service found " + raw);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700474 break;
475 }
476 String name = cooked[2].substring(0, index);
477 String rest = cooked[2].substring(index);
478 String type = rest.replace(".local.", "");
479
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700480 name = unescape(name);
481
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700482 clientInfo.mResolvedService.setServiceName(name);
483 clientInfo.mResolvedService.setServiceType(type);
484 clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
Philip P. Moltmannbbb41dd2016-03-16 10:15:39 -0700485 clientInfo.mResolvedService.setTxtRecords(cooked[6]);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700486
487 stopResolveService(id);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800488 removeRequestMap(clientId, id, clientInfo);
489
490 int id2 = getUniqueId();
491 if (getAddrInfo(id2, cooked[3])) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800492 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800493 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900494 clientInfo.onResolveServiceFailed(
495 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700496 clientInfo.mResolvedService = null;
497 }
498 break;
499 case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
500 /* NNN resolveId errorCode */
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700501 stopResolveService(id);
502 removeRequestMap(clientId, id, clientInfo);
503 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900504 clientInfo.onResolveServiceFailed(
505 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700506 break;
507 case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
508 /* NNN resolveId errorCode */
509 stopGetAddrInfo(id);
510 removeRequestMap(clientId, id, clientInfo);
511 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900512 clientInfo.onResolveServiceFailed(
513 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700514 break;
515 case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
516 /* NNN resolveId hostname ttl addr */
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700517 try {
518 clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900519 clientInfo.onResolveServiceSucceeded(
520 clientId, clientInfo.mResolvedService);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700521 } catch (java.net.UnknownHostException e) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900522 clientInfo.onResolveServiceFailed(
523 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700524 }
525 stopGetAddrInfo(id);
526 removeRequestMap(clientId, id, clientInfo);
527 clientInfo.mResolvedService = null;
528 break;
529 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900530 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700531 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900532 return true;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700533 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700534 }
535 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700536
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700537 private String unescape(String s) {
538 StringBuilder sb = new StringBuilder(s.length());
539 for (int i = 0; i < s.length(); ++i) {
540 char c = s.charAt(i);
541 if (c == '\\') {
542 if (++i >= s.length()) {
paulhub2225702021-11-17 09:35:33 +0800543 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700544 break;
545 }
546 c = s.charAt(i);
547 if (c != '.' && c != '\\') {
548 if (i + 2 >= s.length()) {
paulhub2225702021-11-17 09:35:33 +0800549 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700550 break;
551 }
552 c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
553 i += 2;
554 }
555 }
556 sb.append(c);
557 }
558 return sb.toString();
559 }
560
Hugo Benichi803a2f02017-04-24 11:35:06 +0900561 @VisibleForTesting
paulhu5568f452021-11-30 13:31:29 +0800562 NsdService(Context ctx, Handler handler, DaemonConnectionSupplier fn, long cleanupDelayMs) {
Luke Huang05298582021-06-13 16:52:05 +0000563 mCleanupDelayMs = cleanupDelayMs;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900564 mContext = ctx;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900565 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700566 mNsdStateMachine.start();
Hugo Benichi6d706442017-04-24 16:19:58 +0900567 mDaemonCallback = new NativeCallbackReceiver();
568 mDaemon = fn.get(mDaemonCallback);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700569 }
570
571 public static NsdService create(Context context) throws InterruptedException {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900572 HandlerThread thread = new HandlerThread(TAG);
573 thread.start();
574 Handler handler = new Handler(thread.getLooper());
paulhu5568f452021-11-30 13:31:29 +0800575 NsdService service =
576 new NsdService(context, handler, DaemonConnection::new, CLEANUP_DELAY_MS);
Hugo Benichi6d706442017-04-24 16:19:58 +0900577 service.mDaemonCallback.awaitConnection();
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700578 return service;
579 }
580
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900581 @Override
582 public INsdServiceConnector connect(INsdManagerCallback cb) {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900583 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900584 final INsdServiceConnector connector = new NsdServiceConnector();
585 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
586 NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb)));
587 return connector;
Irfan Sheriff75006652012-04-17 23:15:29 -0700588 }
589
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900590 private static class ListenerArgs {
591 public final NsdServiceConnector connector;
592 public final NsdServiceInfo serviceInfo;
593 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
594 this.connector = connector;
595 this.serviceInfo = serviceInfo;
596 }
597 }
598
599 private class NsdServiceConnector extends INsdServiceConnector.Stub
600 implements IBinder.DeathRecipient {
601 @Override
602 public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
603 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
604 NsdManager.REGISTER_SERVICE, 0, listenerKey,
605 new ListenerArgs(this, serviceInfo)));
606 }
607
608 @Override
609 public void unregisterService(int listenerKey) {
610 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
611 NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
612 new ListenerArgs(this, null)));
613 }
614
615 @Override
616 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
617 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
618 NsdManager.DISCOVER_SERVICES, 0, listenerKey,
619 new ListenerArgs(this, serviceInfo)));
620 }
621
622 @Override
623 public void stopDiscovery(int listenerKey) {
624 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
625 NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
626 }
627
628 @Override
629 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
630 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
631 NsdManager.RESOLVE_SERVICE, 0, listenerKey,
632 new ListenerArgs(this, serviceInfo)));
633 }
634
635 @Override
636 public void startDaemon() {
637 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
638 NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
639 }
640
641 @Override
642 public void binderDied() {
643 mNsdStateMachine.sendMessage(
644 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
645 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700646 }
647
Hugo Benichi912db992017-04-24 16:41:03 +0900648 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff52fc83a2012-04-19 10:26:34 -0700649 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff75006652012-04-17 23:15:29 -0700650 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi912db992017-04-24 16:41:03 +0900651 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
652 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn692107e2012-08-29 18:32:08 -0700653 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff75006652012-04-17 23:15:29 -0700654 }
655
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700656 private int getUniqueId() {
657 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
658 return mUniqueId;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700659 }
660
Sreeram Ramachandranfba15592014-07-19 23:21:46 -0700661 /* These should be in sync with system/netd/server/ResponseCode.h */
Hugo Benichi32be63d2017-04-05 14:06:11 +0900662 static final class NativeResponseCode {
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700663 public static final int SERVICE_DISCOVERY_FAILED = 602;
664 public static final int SERVICE_FOUND = 603;
665 public static final int SERVICE_LOST = 604;
666
667 public static final int SERVICE_REGISTRATION_FAILED = 605;
668 public static final int SERVICE_REGISTERED = 606;
669
670 public static final int SERVICE_RESOLUTION_FAILED = 607;
671 public static final int SERVICE_RESOLVED = 608;
672
673 public static final int SERVICE_UPDATED = 609;
674 public static final int SERVICE_UPDATE_FAILED = 610;
675
676 public static final int SERVICE_GET_ADDR_FAILED = 611;
677 public static final int SERVICE_GET_ADDR_SUCCESS = 612;
Hugo Benichi32be63d2017-04-05 14:06:11 +0900678
679 private static final SparseArray<String> CODE_NAMES = new SparseArray<>();
680 static {
681 CODE_NAMES.put(SERVICE_DISCOVERY_FAILED, "SERVICE_DISCOVERY_FAILED");
682 CODE_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND");
683 CODE_NAMES.put(SERVICE_LOST, "SERVICE_LOST");
684 CODE_NAMES.put(SERVICE_REGISTRATION_FAILED, "SERVICE_REGISTRATION_FAILED");
685 CODE_NAMES.put(SERVICE_REGISTERED, "SERVICE_REGISTERED");
686 CODE_NAMES.put(SERVICE_RESOLUTION_FAILED, "SERVICE_RESOLUTION_FAILED");
687 CODE_NAMES.put(SERVICE_RESOLVED, "SERVICE_RESOLVED");
688 CODE_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
689 CODE_NAMES.put(SERVICE_UPDATE_FAILED, "SERVICE_UPDATE_FAILED");
690 CODE_NAMES.put(SERVICE_GET_ADDR_FAILED, "SERVICE_GET_ADDR_FAILED");
691 CODE_NAMES.put(SERVICE_GET_ADDR_SUCCESS, "SERVICE_GET_ADDR_SUCCESS");
692 }
693
694 static String nameOf(int code) {
695 String name = CODE_NAMES.get(code);
696 if (name == null) {
697 return Integer.toString(code);
698 }
699 return name;
700 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700701 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700702
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700703 private class NativeEvent {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700704 final int code;
705 final String raw;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700706 final String[] cooked;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700707
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700708 NativeEvent(int code, String raw, String[] cooked) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700709 this.code = code;
710 this.raw = raw;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700711 this.cooked = cooked;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700712 }
713 }
714
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700715 class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
Hugo Benichi6d706442017-04-24 16:19:58 +0900716 private final CountDownLatch connected = new CountDownLatch(1);
717
718 public void awaitConnection() throws InterruptedException {
719 connected.await();
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700720 }
721
Hugo Benichi6d706442017-04-24 16:19:58 +0900722 @Override
723 public void onDaemonConnected() {
724 connected.countDown();
725 }
726
727 @Override
Dianne Hackborn9cd0fea2014-02-26 16:20:52 -0800728 public boolean onCheckHoldWakeLock(int code) {
729 return false;
730 }
731
Hugo Benichi6d706442017-04-24 16:19:58 +0900732 @Override
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700733 public boolean onEvent(int code, String raw, String[] cooked) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700734 // TODO: NDC translates a message to a callback, we could enhance NDC to
735 // directly interact with a state machine through messages
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700736 NativeEvent event = new NativeEvent(code, raw, cooked);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700737 mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
738 return true;
739 }
740 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700741
Hugo Benichi6d706442017-04-24 16:19:58 +0900742 interface DaemonConnectionSupplier {
743 DaemonConnection get(NativeCallbackReceiver callback);
744 }
745
746 @VisibleForTesting
747 public static class DaemonConnection {
748 final NativeDaemonConnector mNativeConnector;
Luke Huang05298582021-06-13 16:52:05 +0000749 boolean mIsStarted = false;
Hugo Benichi6d706442017-04-24 16:19:58 +0900750
751 DaemonConnection(NativeCallbackReceiver callback) {
752 mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
753 new Thread(mNativeConnector, MDNS_TAG).start();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700754 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900755
Luke Huang05298582021-06-13 16:52:05 +0000756 /**
757 * Executes the specified cmd on the daemon.
758 */
Hugo Benichi6d706442017-04-24 16:19:58 +0900759 public boolean execute(Object... args) {
760 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800761 Log.d(TAG, "mdnssd " + Arrays.toString(args));
Hugo Benichi6d706442017-04-24 16:19:58 +0900762 }
763 try {
764 mNativeConnector.execute("mdnssd", args);
765 } catch (NativeDaemonConnectorException e) {
paulhub2225702021-11-17 09:35:33 +0800766 Log.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
Hugo Benichi6d706442017-04-24 16:19:58 +0900767 return false;
768 }
769 return true;
770 }
771
Luke Huang05298582021-06-13 16:52:05 +0000772 /**
773 * Starts the daemon if it is not already started.
774 */
775 public void maybeStart() {
776 if (mIsStarted) {
777 return;
778 }
Hugo Benichi4dd4db72017-04-28 15:31:10 +0900779 execute("start-service");
Luke Huang05298582021-06-13 16:52:05 +0000780 mIsStarted = true;
Hugo Benichi6d706442017-04-24 16:19:58 +0900781 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900782
Luke Huang05298582021-06-13 16:52:05 +0000783 /**
784 * Stops the daemon if it is started.
785 */
786 public void maybeStop() {
787 if (!mIsStarted) {
788 return;
789 }
Hugo Benichi4dd4db72017-04-28 15:31:10 +0900790 execute("stop-service");
Luke Huang05298582021-06-13 16:52:05 +0000791 mIsStarted = false;
Hugo Benichi4dd4db72017-04-28 15:31:10 +0900792 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700793 }
794
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700795 private boolean registerService(int regId, NsdServiceInfo service) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900796 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800797 Log.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700798 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900799 String name = service.getServiceName();
800 String type = service.getServiceType();
801 int port = service.getPort();
802 byte[] textRecord = service.getTxtRecord();
803 String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
Hugo Benichi4dd4db72017-04-28 15:31:10 +0900804 return mDaemon.execute("register", regId, name, type, port, record);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700805 }
806
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700807 private boolean unregisterService(int regId) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900808 return mDaemon.execute("stop-register", regId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700809 }
810
811 private boolean updateService(int regId, DnsSdTxtRecord t) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900812 if (t == null) {
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700813 return false;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700814 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900815 return mDaemon.execute("update", regId, t.size(), t.getRawData());
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700816 }
817
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700818 private boolean discoverServices(int discoveryId, String serviceType) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900819 return mDaemon.execute("discover", discoveryId, serviceType);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700820 }
821
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700822 private boolean stopServiceDiscovery(int discoveryId) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900823 return mDaemon.execute("stop-discover", discoveryId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700824 }
825
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700826 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900827 String name = service.getServiceName();
828 String type = service.getServiceType();
829 return mDaemon.execute("resolve", resolveId, name, type, "local.");
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700830 }
831
832 private boolean stopResolveService(int resolveId) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900833 return mDaemon.execute("stop-resolve", resolveId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700834 }
835
836 private boolean getAddrInfo(int resolveId, String hostname) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900837 return mDaemon.execute("getaddrinfo", resolveId, hostname);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700838 }
839
840 private boolean stopGetAddrInfo(int resolveId) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900841 return mDaemon.execute("stop-getaddrinfo", resolveId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700842 }
843
844 @Override
Irfan Sheriff75006652012-04-17 23:15:29 -0700845 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
paulhub2225702021-11-17 09:35:33 +0800846 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
847 != PackageManager.PERMISSION_GRANTED) {
848 pw.println("Permission Denial: can't dump " + TAG
849 + " due to missing android.permission.DUMP permission");
850 return;
851 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700852
Irfan Sheriff75006652012-04-17 23:15:29 -0700853 for (ClientInfo client : mClients.values()) {
854 pw.println("Client Info");
855 pw.println(client);
856 }
857
858 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700859 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700860
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700861 /* Information tracked per client */
862 private class ClientInfo {
863
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700864 private static final int MAX_LIMIT = 10;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900865 private final INsdManagerCallback mCb;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700866 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700867 private NsdServiceInfo mResolvedService;
868
869 /* A map from client id to unique id sent to mDns */
Hugo Benichid2552ae2017-04-11 14:42:47 +0900870 private final SparseIntArray mClientIds = new SparseIntArray();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700871
Dave Plattfeff2af2014-03-07 14:48:22 -0800872 /* A map from client id to the type of the request we had received */
Hugo Benichid2552ae2017-04-11 14:42:47 +0900873 private final SparseIntArray mClientRequests = new SparseIntArray();
Dave Plattfeff2af2014-03-07 14:48:22 -0800874
Luke Huangf7277ed2021-07-12 21:15:10 +0800875 // The target SDK of this client < Build.VERSION_CODES.S
876 private boolean mIsLegacy = false;
877
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900878 private ClientInfo(INsdManagerCallback cb) {
879 mCb = cb;
paulhub2225702021-11-17 09:35:33 +0800880 if (DBG) Log.d(TAG, "New client");
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700881 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700882
883 @Override
884 public String toString() {
Jeff Sharkey63465382020-10-17 21:20:13 -0600885 StringBuilder sb = new StringBuilder();
Irfan Sheriff75006652012-04-17 23:15:29 -0700886 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Luke Huangf7277ed2021-07-12 21:15:10 +0800887 sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700888 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800889 int clientID = mClientIds.keyAt(i);
890 sb.append("clientId ").append(clientID).
891 append(" mDnsId ").append(mClientIds.valueAt(i)).
892 append(" type ").append(mClientRequests.get(clientID)).append("\n");
Irfan Sheriff75006652012-04-17 23:15:29 -0700893 }
894 return sb.toString();
895 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800896
Luke Huangf7277ed2021-07-12 21:15:10 +0800897 private boolean isLegacy() {
898 return mIsLegacy;
899 }
900
901 private void setLegacy() {
902 mIsLegacy = true;
903 }
904
Dave Plattfeff2af2014-03-07 14:48:22 -0800905 // Remove any pending requests from the global map when we get rid of a client,
906 // and send cancellations to the daemon.
907 private void expungeAllRequests() {
908 int globalId, clientId, i;
Hugo Benichid2552ae2017-04-11 14:42:47 +0900909 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Dave Plattfeff2af2014-03-07 14:48:22 -0800910 for (i = 0; i < mClientIds.size(); i++) {
911 clientId = mClientIds.keyAt(i);
912 globalId = mClientIds.valueAt(i);
913 mIdToClientInfoMap.remove(globalId);
paulhub2225702021-11-17 09:35:33 +0800914 if (DBG) {
915 Log.d(TAG, "Terminating client-ID " + clientId
916 + " global-ID " + globalId + " type " + mClientRequests.get(clientId));
917 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800918 switch (mClientRequests.get(clientId)) {
919 case NsdManager.DISCOVER_SERVICES:
920 stopServiceDiscovery(globalId);
921 break;
922 case NsdManager.RESOLVE_SERVICE:
923 stopResolveService(globalId);
924 break;
925 case NsdManager.REGISTER_SERVICE:
926 unregisterService(globalId);
927 break;
928 default:
929 break;
930 }
931 }
932 mClientIds.clear();
933 mClientRequests.clear();
934 }
935
Christopher Lane74411222014-04-25 18:39:07 -0700936 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
937 // return the corresponding listener id. mDnsClient id is also called a global id.
938 private int getClientId(final int globalId) {
Hugo Benichid2552ae2017-04-11 14:42:47 +0900939 int idx = mClientIds.indexOfValue(globalId);
940 if (idx < 0) {
941 return idx;
Christopher Lane74411222014-04-25 18:39:07 -0700942 }
Hugo Benichid2552ae2017-04-11 14:42:47 +0900943 return mClientIds.keyAt(idx);
Christopher Lane74411222014-04-25 18:39:07 -0700944 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900945
946 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
947 try {
948 mCb.onDiscoverServicesStarted(listenerKey, info);
949 } catch (RemoteException e) {
950 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
951 }
952 }
953
954 void onDiscoverServicesFailed(int listenerKey, int error) {
955 try {
956 mCb.onDiscoverServicesFailed(listenerKey, error);
957 } catch (RemoteException e) {
958 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
959 }
960 }
961
962 void onServiceFound(int listenerKey, NsdServiceInfo info) {
963 try {
964 mCb.onServiceFound(listenerKey, info);
965 } catch (RemoteException e) {
966 Log.e(TAG, "Error calling onServiceFound(", e);
967 }
968 }
969
970 void onServiceLost(int listenerKey, NsdServiceInfo info) {
971 try {
972 mCb.onServiceLost(listenerKey, info);
973 } catch (RemoteException e) {
974 Log.e(TAG, "Error calling onServiceLost(", e);
975 }
976 }
977
978 void onStopDiscoveryFailed(int listenerKey, int error) {
979 try {
980 mCb.onStopDiscoveryFailed(listenerKey, error);
981 } catch (RemoteException e) {
982 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
983 }
984 }
985
986 void onStopDiscoverySucceeded(int listenerKey) {
987 try {
988 mCb.onStopDiscoverySucceeded(listenerKey);
989 } catch (RemoteException e) {
990 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
991 }
992 }
993
994 void onRegisterServiceFailed(int listenerKey, int error) {
995 try {
996 mCb.onRegisterServiceFailed(listenerKey, error);
997 } catch (RemoteException e) {
998 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
999 }
1000 }
1001
1002 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1003 try {
1004 mCb.onRegisterServiceSucceeded(listenerKey, info);
1005 } catch (RemoteException e) {
1006 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
1007 }
1008 }
1009
1010 void onUnregisterServiceFailed(int listenerKey, int error) {
1011 try {
1012 mCb.onUnregisterServiceFailed(listenerKey, error);
1013 } catch (RemoteException e) {
1014 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
1015 }
1016 }
1017
1018 void onUnregisterServiceSucceeded(int listenerKey) {
1019 try {
1020 mCb.onUnregisterServiceSucceeded(listenerKey);
1021 } catch (RemoteException e) {
1022 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
1023 }
1024 }
1025
1026 void onResolveServiceFailed(int listenerKey, int error) {
1027 try {
1028 mCb.onResolveServiceFailed(listenerKey, error);
1029 } catch (RemoteException e) {
1030 Log.e(TAG, "Error calling onResolveServiceFailed", e);
1031 }
1032 }
1033
1034 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1035 try {
1036 mCb.onResolveServiceSucceeded(listenerKey, info);
1037 } catch (RemoteException e) {
1038 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
1039 }
1040 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001041 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001042}