blob: e37aa6362ecffefbac0b1dffc55f2617028093d2 [file] [log] [blame]
Irfan Sheriff7d024d32012-03-22 17:01:39 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
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
19import android.content.Context;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070020import android.content.ContentResolver;
21import android.content.Intent;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070022import android.content.pm.PackageManager;
Irfan Sheriff919aca52012-06-01 16:44:13 -070023import android.database.ContentObserver;
Irfan Sheriff22af38c2012-05-03 16:44:27 -070024import android.net.nsd.NsdServiceInfo;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070025import android.net.nsd.DnsSdTxtRecord;
26import android.net.nsd.INsdManager;
27import android.net.nsd.NsdManager;
28import android.os.Binder;
Hugo Benichicbb13672017-04-24 11:35:06 +090029import android.os.HandlerThread;
30import android.os.Handler;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070031import android.os.Message;
32import android.os.Messenger;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070033import android.os.UserHandle;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070034import android.provider.Settings;
Philip P. Moltmann312c61e2016-03-16 10:15:39 -070035import android.util.Base64;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070036import android.util.Slog;
Irfan Sheriff22af38c2012-05-03 16:44:27 -070037import android.util.SparseArray;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070038
39import java.io.FileDescriptor;
40import java.io.PrintWriter;
Irfan Sheriff817388e2012-04-11 14:52:19 -070041import java.net.InetAddress;
Irfan Sheriff817388e2012-04-11 14:52:19 -070042import java.util.HashMap;
Irfan Sheriff817388e2012-04-11 14:52:19 -070043import java.util.concurrent.CountDownLatch;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070044
Hugo Benichicbb13672017-04-24 11:35:06 +090045import com.android.internal.annotations.VisibleForTesting;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070046import com.android.internal.util.AsyncChannel;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070047import com.android.internal.util.Protocol;
48import com.android.internal.util.State;
49import com.android.internal.util.StateMachine;
Christopher Lane771cd652014-04-14 16:31:27 -070050import com.android.server.NativeDaemonConnector.Command;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070051
52/**
53 * Network Service Discovery Service handles remote service discovery operation requests by
54 * implementing the INsdManager interface.
55 *
56 * @hide
57 */
58public class NsdService extends INsdManager.Stub {
59 private static final String TAG = "NsdService";
60 private static final String MDNS_TAG = "mDnsConnector";
61
Hugo Benichi2183ba92017-04-05 14:06:11 +090062 private static final boolean DBG = true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070063
Hugo Benichi2183ba92017-04-05 14:06:11 +090064 private final Context mContext;
Hugo Benichicbb13672017-04-24 11:35:06 +090065 private final NsdSettings mNsdSettings;
Hugo Benichi2183ba92017-04-05 14:06:11 +090066 private final NsdStateMachine mNsdStateMachine;
67 private final NativeDaemonConnector mNativeConnector;
68 private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1);
Irfan Sheriff7d024d32012-03-22 17:01:39 -070069
70 /**
71 * Clients receiving asynchronous messages
72 */
Hugo Benichi2183ba92017-04-05 14:06:11 +090073 private final HashMap<Messenger, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff7d024d32012-03-22 17:01:39 -070074
Irfan Sheriff22af38c2012-05-03 16:44:27 -070075 /* A map from unique id to client info */
Hugo Benichi2183ba92017-04-05 14:06:11 +090076 private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
Irfan Sheriff22af38c2012-05-03 16:44:27 -070077
Hugo Benichi2183ba92017-04-05 14:06:11 +090078 private final AsyncChannel mReplyChannel = new AsyncChannel();
Irfan Sheriff7d024d32012-03-22 17:01:39 -070079
Hugo Benichi2183ba92017-04-05 14:06:11 +090080 private static final int INVALID_ID = 0;
Irfan Sheriff817388e2012-04-11 14:52:19 -070081 private int mUniqueId = 1;
82
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070083 private class NsdStateMachine extends StateMachine {
84
Irfan Sheriff22af38c2012-05-03 16:44:27 -070085 private final DefaultState mDefaultState = new DefaultState();
86 private final DisabledState mDisabledState = new DisabledState();
87 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070088
89 @Override
Wink Savillebbf30dfd2012-05-29 12:40:46 -070090 protected String getWhatToString(int what) {
Hugo Benichi2183ba92017-04-05 14:06:11 +090091 return NsdManager.nameOf(what);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070092 }
93
Irfan Sheriff919aca52012-06-01 16:44:13 -070094 /**
95 * Observes the NSD on/off setting, and takes action when changed.
96 */
97 private void registerForNsdSetting() {
98 ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
99 @Override
Hugo Benichi2183ba92017-04-05 14:06:11 +0900100 public void onChange(boolean selfChange) {
101 if (isNsdEnabled()) {
102 mNsdStateMachine.sendMessage(NsdManager.ENABLE);
103 } else {
104 mNsdStateMachine.sendMessage(NsdManager.DISABLE);
Irfan Sheriff919aca52012-06-01 16:44:13 -0700105 }
Hugo Benichi2183ba92017-04-05 14:06:11 +0900106 }
Irfan Sheriff919aca52012-06-01 16:44:13 -0700107 };
108
109 mContext.getContentResolver().registerContentObserver(
Jeff Sharkey625239a2012-09-26 22:03:49 -0700110 Settings.Global.getUriFor(Settings.Global.NSD_ON),
Irfan Sheriff919aca52012-06-01 16:44:13 -0700111 false, contentObserver);
112 }
113
Hugo Benichicbb13672017-04-24 11:35:06 +0900114 NsdStateMachine(String name, Handler handler) {
115 super(name, handler);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700116 addState(mDefaultState);
117 addState(mDisabledState, mDefaultState);
118 addState(mEnabledState, mDefaultState);
119 if (isNsdEnabled()) {
120 setInitialState(mEnabledState);
121 } else {
122 setInitialState(mDisabledState);
123 }
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700124 setLogRecSize(25);
Irfan Sheriff919aca52012-06-01 16:44:13 -0700125 registerForNsdSetting();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700126 }
127
128 class DefaultState extends State {
129 @Override
130 public boolean processMessage(Message msg) {
Dave Plattf31760c2014-03-07 14:48:22 -0800131 ClientInfo cInfo = null;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700132 switch (msg.what) {
133 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
134 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
135 AsyncChannel c = (AsyncChannel) msg.obj;
136 if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
137 c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
Dave Plattf31760c2014-03-07 14:48:22 -0800138 cInfo = new ClientInfo(c, msg.replyTo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700139 mClients.put(msg.replyTo, cInfo);
140 } else {
141 Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
142 }
143 break;
144 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Dave Plattf31760c2014-03-07 14:48:22 -0800145 switch (msg.arg1) {
146 case AsyncChannel.STATUS_SEND_UNSUCCESSFUL:
147 Slog.e(TAG, "Send failed, client connection lost");
148 break;
149 case AsyncChannel.STATUS_REMOTE_DISCONNECTION:
150 if (DBG) Slog.d(TAG, "Client disconnected");
151 break;
152 default:
153 if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
154 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700155 }
Dave Plattf31760c2014-03-07 14:48:22 -0800156 cInfo = mClients.get(msg.replyTo);
157 if (cInfo != null) {
158 cInfo.expungeAllRequests();
159 mClients.remove(msg.replyTo);
160 }
161 //Last client
162 if (mClients.size() == 0) {
163 stopMDnsDaemon();
164 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700165 break;
166 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
167 AsyncChannel ac = new AsyncChannel();
168 ac.connect(mContext, getHandler(), msg.replyTo);
169 break;
170 case NsdManager.DISCOVER_SERVICES:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700171 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
172 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700173 break;
174 case NsdManager.STOP_DISCOVERY:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700175 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
176 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700177 break;
178 case NsdManager.REGISTER_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700179 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
180 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700181 break;
182 case NsdManager.UNREGISTER_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700183 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
184 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700185 break;
186 case NsdManager.RESOLVE_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700187 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
188 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700189 break;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700190 case NsdManager.NATIVE_DAEMON_EVENT:
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700191 default:
192 Slog.e(TAG, "Unhandled " + msg);
193 return NOT_HANDLED;
194 }
195 return HANDLED;
196 }
197 }
198
199 class DisabledState extends State {
200 @Override
201 public void enter() {
202 sendNsdStateChangeBroadcast(false);
203 }
204
205 @Override
206 public boolean processMessage(Message msg) {
207 switch (msg.what) {
208 case NsdManager.ENABLE:
209 transitionTo(mEnabledState);
210 break;
211 default:
212 return NOT_HANDLED;
213 }
214 return HANDLED;
215 }
216 }
217
218 class EnabledState extends State {
219 @Override
220 public void enter() {
221 sendNsdStateChangeBroadcast(true);
222 if (mClients.size() > 0) {
223 startMDnsDaemon();
224 }
225 }
226
227 @Override
228 public void exit() {
229 if (mClients.size() > 0) {
230 stopMDnsDaemon();
231 }
232 }
233
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700234 private boolean requestLimitReached(ClientInfo clientInfo) {
235 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
236 if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
237 return true;
238 }
239 return false;
240 }
241
Dave Plattf31760c2014-03-07 14:48:22 -0800242 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700243 clientInfo.mClientIds.put(clientId, globalId);
Dave Plattf31760c2014-03-07 14:48:22 -0800244 clientInfo.mClientRequests.put(clientId, what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700245 mIdToClientInfoMap.put(globalId, clientInfo);
246 }
247
248 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
249 clientInfo.mClientIds.remove(clientId);
Dave Plattf31760c2014-03-07 14:48:22 -0800250 clientInfo.mClientRequests.remove(clientId);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700251 mIdToClientInfoMap.remove(globalId);
252 }
253
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700254 @Override
255 public boolean processMessage(Message msg) {
256 ClientInfo clientInfo;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700257 NsdServiceInfo servInfo;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700258 int id;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700259 switch (msg.what) {
Hugo Benichi23dba852017-04-05 14:43:29 +0900260 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700261 //First client
262 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL &&
263 mClients.size() == 0) {
264 startMDnsDaemon();
265 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900266 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700267 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Hugo Benichi23dba852017-04-05 14:43:29 +0900268 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700269 case NsdManager.DISABLE:
270 //TODO: cleanup clients
271 transitionTo(mDisabledState);
272 break;
273 case NsdManager.DISCOVER_SERVICES:
274 if (DBG) Slog.d(TAG, "Discover services");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700275 servInfo = (NsdServiceInfo) msg.obj;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700276 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700277
278 if (requestLimitReached(clientInfo)) {
279 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
280 NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700281 break;
282 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700283
284 id = getUniqueId();
285 if (discoverServices(id, servInfo.getServiceType())) {
286 if (DBG) {
287 Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
288 servInfo.getServiceType());
289 }
Dave Plattf31760c2014-03-07 14:48:22 -0800290 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700291 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700292 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700293 stopServiceDiscovery(id);
294 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
295 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700296 }
297 break;
298 case NsdManager.STOP_DISCOVERY:
299 if (DBG) Slog.d(TAG, "Stop service discovery");
300 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700301
302 try {
303 id = clientInfo.mClientIds.get(msg.arg2).intValue();
304 } catch (NullPointerException e) {
305 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
306 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700307 break;
308 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700309 removeRequestMap(msg.arg2, id, clientInfo);
310 if (stopServiceDiscovery(id)) {
311 replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700312 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700313 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
314 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700315 }
316 break;
317 case NsdManager.REGISTER_SERVICE:
318 if (DBG) Slog.d(TAG, "Register service");
319 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700320 if (requestLimitReached(clientInfo)) {
321 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
322 NsdManager.FAILURE_MAX_LIMIT);
323 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700324 }
325
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700326 id = getUniqueId();
327 if (registerService(id, (NsdServiceInfo) msg.obj)) {
328 if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
Dave Plattf31760c2014-03-07 14:48:22 -0800329 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700330 // Return success after mDns reports success
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700331 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700332 unregisterService(id);
333 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
334 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700335 }
336 break;
337 case NsdManager.UNREGISTER_SERVICE:
338 if (DBG) Slog.d(TAG, "unregister service");
339 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700340 try {
341 id = clientInfo.mClientIds.get(msg.arg2).intValue();
342 } catch (NullPointerException e) {
343 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
344 NsdManager.FAILURE_INTERNAL_ERROR);
345 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700346 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700347 removeRequestMap(msg.arg2, id, clientInfo);
348 if (unregisterService(id)) {
349 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
350 } else {
351 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
352 NsdManager.FAILURE_INTERNAL_ERROR);
353 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700354 break;
355 case NsdManager.RESOLVE_SERVICE:
356 if (DBG) Slog.d(TAG, "Resolve service");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700357 servInfo = (NsdServiceInfo) msg.obj;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700358 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700359
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700360
361 if (clientInfo.mResolvedService != null) {
362 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
363 NsdManager.FAILURE_ALREADY_ACTIVE);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700364 break;
365 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700366
367 id = getUniqueId();
368 if (resolveService(id, servInfo)) {
369 clientInfo.mResolvedService = new NsdServiceInfo();
Dave Plattf31760c2014-03-07 14:48:22 -0800370 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700371 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700372 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
373 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700374 }
375 break;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700376 case NsdManager.NATIVE_DAEMON_EVENT:
377 NativeEvent event = (NativeEvent) msg.obj;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700378 if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
Hugo Benichi23dba852017-04-05 14:43:29 +0900379 return NOT_HANDLED;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700380 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700381 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700382 default:
Hugo Benichi23dba852017-04-05 14:43:29 +0900383 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700384 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900385 return HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700386 }
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700387
388 private boolean handleNativeEvent(int code, String raw, String[] cooked) {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700389 NsdServiceInfo servInfo;
390 int id = Integer.parseInt(cooked[1]);
391 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
392 if (clientInfo == null) {
Hugo Benichi2183ba92017-04-05 14:06:11 +0900393 String name = NativeResponseCode.nameOf(code);
394 Slog.e(TAG, String.format("id %d for %s has no client mapping", id, name));
Hugo Benichi23dba852017-04-05 14:43:29 +0900395 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700396 }
397
398 /* This goes in response as msg.arg2 */
Christopher Lane5a577902014-04-25 18:39:07 -0700399 int clientId = clientInfo.getClientId(id);
400 if (clientId < 0) {
Vinit Deshapnde8ed09e82013-06-25 19:45:03 -0700401 // This can happen because of race conditions. For example,
402 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
403 // and we may get in this situation.
Hugo Benichi2183ba92017-04-05 14:06:11 +0900404 String name = NativeResponseCode.nameOf(code);
405 Slog.d(TAG, String.format(
406 "Notification %s for listener id %d that is no longer active",
407 name, id));
Hugo Benichi23dba852017-04-05 14:43:29 +0900408 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700409 }
Hugo Benichi2183ba92017-04-05 14:06:11 +0900410 if (DBG) {
411 String name = NativeResponseCode.nameOf(code);
412 Slog.d(TAG, String.format("Native daemon message %s: %s", name, raw));
413 }
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700414 switch (code) {
415 case NativeResponseCode.SERVICE_FOUND:
416 /* NNN uniqueId serviceName regType domain */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700417 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700418 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
419 clientId, servInfo);
420 break;
421 case NativeResponseCode.SERVICE_LOST:
422 /* NNN uniqueId serviceName regType domain */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700423 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700424 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
425 clientId, servInfo);
426 break;
427 case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
428 /* NNN uniqueId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700429 clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
430 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
431 break;
432 case NativeResponseCode.SERVICE_REGISTERED:
433 /* NNN regId serviceName regType */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700434 servInfo = new NsdServiceInfo(cooked[2], null);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700435 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
436 id, clientId, servInfo);
437 break;
438 case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
439 /* NNN regId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700440 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
441 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
442 break;
443 case NativeResponseCode.SERVICE_UPDATED:
444 /* NNN regId */
445 break;
446 case NativeResponseCode.SERVICE_UPDATE_FAILED:
447 /* NNN regId errorCode */
448 break;
449 case NativeResponseCode.SERVICE_RESOLVED:
450 /* NNN resolveId fullName hostName port txtlen txtdata */
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700451 int index = 0;
452 while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
453 if (cooked[2].charAt(index) == '\\') {
454 ++index;
455 }
456 ++index;
457 }
458 if (index >= cooked[2].length()) {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700459 Slog.e(TAG, "Invalid service found " + raw);
460 break;
461 }
462 String name = cooked[2].substring(0, index);
463 String rest = cooked[2].substring(index);
464 String type = rest.replace(".local.", "");
465
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700466 name = unescape(name);
467
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700468 clientInfo.mResolvedService.setServiceName(name);
469 clientInfo.mResolvedService.setServiceType(type);
470 clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
Philip P. Moltmann312c61e2016-03-16 10:15:39 -0700471 clientInfo.mResolvedService.setTxtRecords(cooked[6]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700472
473 stopResolveService(id);
Vinit Deshapnde40387242013-11-12 15:36:37 -0800474 removeRequestMap(clientId, id, clientInfo);
475
476 int id2 = getUniqueId();
477 if (getAddrInfo(id2, cooked[3])) {
Dave Plattf31760c2014-03-07 14:48:22 -0800478 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
Vinit Deshapnde40387242013-11-12 15:36:37 -0800479 } else {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700480 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
481 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700482 clientInfo.mResolvedService = null;
483 }
484 break;
485 case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
486 /* NNN resolveId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700487 stopResolveService(id);
488 removeRequestMap(clientId, id, clientInfo);
489 clientInfo.mResolvedService = null;
490 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
491 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
492 break;
493 case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
494 /* NNN resolveId errorCode */
495 stopGetAddrInfo(id);
496 removeRequestMap(clientId, id, clientInfo);
497 clientInfo.mResolvedService = null;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700498 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
499 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
500 break;
501 case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
502 /* NNN resolveId hostname ttl addr */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700503 try {
504 clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
505 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
506 0, clientId, clientInfo.mResolvedService);
507 } catch (java.net.UnknownHostException e) {
508 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
509 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
510 }
511 stopGetAddrInfo(id);
512 removeRequestMap(clientId, id, clientInfo);
513 clientInfo.mResolvedService = null;
514 break;
515 default:
Hugo Benichi23dba852017-04-05 14:43:29 +0900516 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700517 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900518 return true;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700519 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700520 }
521 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700522
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700523 private String unescape(String s) {
524 StringBuilder sb = new StringBuilder(s.length());
525 for (int i = 0; i < s.length(); ++i) {
526 char c = s.charAt(i);
527 if (c == '\\') {
528 if (++i >= s.length()) {
529 Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
530 break;
531 }
532 c = s.charAt(i);
533 if (c != '.' && c != '\\') {
534 if (i + 2 >= s.length()) {
535 Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
536 break;
537 }
538 c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
539 i += 2;
540 }
541 }
542 sb.append(c);
543 }
544 return sb.toString();
545 }
546
Hugo Benichicbb13672017-04-24 11:35:06 +0900547 @VisibleForTesting
548 NsdService(Context ctx, NsdSettings settings, Handler handler) {
549 mContext = ctx;
550 mNsdSettings = settings;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700551
Hugo Benichicbb13672017-04-24 11:35:06 +0900552 NativeCallbackReceiver callback = new NativeCallbackReceiver();
553 mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700554
Hugo Benichicbb13672017-04-24 11:35:06 +0900555 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700556 mNsdStateMachine.start();
557
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700558 Thread th = new Thread(mNativeConnector, MDNS_TAG);
559 th.start();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700560 }
561
562 public static NsdService create(Context context) throws InterruptedException {
Hugo Benichicbb13672017-04-24 11:35:06 +0900563 NsdSettings settings = NsdSettings.makeDefault(context);
564 HandlerThread thread = new HandlerThread(TAG);
565 thread.start();
566 Handler handler = new Handler(thread.getLooper());
567 NsdService service = new NsdService(context, settings, handler);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700568 service.mNativeDaemonConnected.await();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700569 return service;
570 }
571
572 public Messenger getMessenger() {
Hugo Benichicbb13672017-04-24 11:35:06 +0900573 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700574 return new Messenger(mNsdStateMachine.getHandler());
575 }
576
577 public void setEnabled(boolean enable) {
578 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
579 "NsdService");
Hugo Benichicbb13672017-04-24 11:35:06 +0900580 mNsdSettings.putEnabledStatus(enable);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700581 if (enable) {
582 mNsdStateMachine.sendMessage(NsdManager.ENABLE);
583 } else {
584 mNsdStateMachine.sendMessage(NsdManager.DISABLE);
585 }
586 }
587
588 private void sendNsdStateChangeBroadcast(boolean enabled) {
Irfan Sheriff54ac7a52012-04-19 10:26:34 -0700589 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700590 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
591 if (enabled) {
592 intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_ENABLED);
593 } else {
594 intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED);
595 }
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700596 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700597 }
598
599 private boolean isNsdEnabled() {
Hugo Benichicbb13672017-04-24 11:35:06 +0900600 boolean ret = mNsdSettings.isEnabled();
601 if (DBG) {
602 Slog.d(TAG, "Network service discovery is " + (ret ? "enabled" : "disabled"));
603 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700604 return ret;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700605 }
606
Irfan Sheriff817388e2012-04-11 14:52:19 -0700607 private int getUniqueId() {
608 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
609 return mUniqueId;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700610 }
611
Sreeram Ramachandran03666c72014-07-19 23:21:46 -0700612 /* These should be in sync with system/netd/server/ResponseCode.h */
Hugo Benichi2183ba92017-04-05 14:06:11 +0900613 static final class NativeResponseCode {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700614 public static final int SERVICE_DISCOVERY_FAILED = 602;
615 public static final int SERVICE_FOUND = 603;
616 public static final int SERVICE_LOST = 604;
617
618 public static final int SERVICE_REGISTRATION_FAILED = 605;
619 public static final int SERVICE_REGISTERED = 606;
620
621 public static final int SERVICE_RESOLUTION_FAILED = 607;
622 public static final int SERVICE_RESOLVED = 608;
623
624 public static final int SERVICE_UPDATED = 609;
625 public static final int SERVICE_UPDATE_FAILED = 610;
626
627 public static final int SERVICE_GET_ADDR_FAILED = 611;
628 public static final int SERVICE_GET_ADDR_SUCCESS = 612;
Hugo Benichi2183ba92017-04-05 14:06:11 +0900629
630 private static final SparseArray<String> CODE_NAMES = new SparseArray<>();
631 static {
632 CODE_NAMES.put(SERVICE_DISCOVERY_FAILED, "SERVICE_DISCOVERY_FAILED");
633 CODE_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND");
634 CODE_NAMES.put(SERVICE_LOST, "SERVICE_LOST");
635 CODE_NAMES.put(SERVICE_REGISTRATION_FAILED, "SERVICE_REGISTRATION_FAILED");
636 CODE_NAMES.put(SERVICE_REGISTERED, "SERVICE_REGISTERED");
637 CODE_NAMES.put(SERVICE_RESOLUTION_FAILED, "SERVICE_RESOLUTION_FAILED");
638 CODE_NAMES.put(SERVICE_RESOLVED, "SERVICE_RESOLVED");
639 CODE_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
640 CODE_NAMES.put(SERVICE_UPDATE_FAILED, "SERVICE_UPDATE_FAILED");
641 CODE_NAMES.put(SERVICE_GET_ADDR_FAILED, "SERVICE_GET_ADDR_FAILED");
642 CODE_NAMES.put(SERVICE_GET_ADDR_SUCCESS, "SERVICE_GET_ADDR_SUCCESS");
643 }
644
645 static String nameOf(int code) {
646 String name = CODE_NAMES.get(code);
647 if (name == null) {
648 return Integer.toString(code);
649 }
650 return name;
651 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700652 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700653
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700654 private class NativeEvent {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700655 final int code;
656 final String raw;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700657 final String[] cooked;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700658
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700659 NativeEvent(int code, String raw, String[] cooked) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700660 this.code = code;
661 this.raw = raw;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700662 this.cooked = cooked;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700663 }
664 }
665
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700666 class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
667 public void onDaemonConnected() {
668 mNativeDaemonConnected.countDown();
669 }
670
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800671 public boolean onCheckHoldWakeLock(int code) {
672 return false;
673 }
674
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700675 public boolean onEvent(int code, String raw, String[] cooked) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700676 // TODO: NDC translates a message to a callback, we could enhance NDC to
677 // directly interact with a state machine through messages
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700678 NativeEvent event = new NativeEvent(code, raw, cooked);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700679 mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
680 return true;
681 }
682 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700683
Irfan Sheriff817388e2012-04-11 14:52:19 -0700684 private boolean startMDnsDaemon() {
685 if (DBG) Slog.d(TAG, "startMDnsDaemon");
686 try {
687 mNativeConnector.execute("mdnssd", "start-service");
688 } catch(NativeDaemonConnectorException e) {
689 Slog.e(TAG, "Failed to start daemon" + e);
690 return false;
691 }
692 return true;
693 }
694
695 private boolean stopMDnsDaemon() {
696 if (DBG) Slog.d(TAG, "stopMDnsDaemon");
697 try {
698 mNativeConnector.execute("mdnssd", "stop-service");
699 } catch(NativeDaemonConnectorException e) {
700 Slog.e(TAG, "Failed to start daemon" + e);
701 return false;
702 }
703 return true;
704 }
705
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700706 private boolean registerService(int regId, NsdServiceInfo service) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700707 if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700708 try {
Christopher Laneb72d8b42014-03-17 16:35:45 -0700709 Command cmd = new Command("mdnssd", "register", regId, service.getServiceName(),
Philip P. Moltmann312c61e2016-03-16 10:15:39 -0700710 service.getServiceType(), service.getPort(),
711 Base64.encodeToString(service.getTxtRecord(), Base64.DEFAULT)
712 .replace("\n", ""));
Christopher Laneb72d8b42014-03-17 16:35:45 -0700713
714 mNativeConnector.execute(cmd);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700715 } catch(NativeDaemonConnectorException e) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700716 Slog.e(TAG, "Failed to execute registerService " + e);
717 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700718 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700719 return true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700720 }
721
Irfan Sheriff817388e2012-04-11 14:52:19 -0700722 private boolean unregisterService(int regId) {
723 if (DBG) Slog.d(TAG, "unregisterService: " + regId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700724 try {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700725 mNativeConnector.execute("mdnssd", "stop-register", regId);
726 } catch(NativeDaemonConnectorException e) {
727 Slog.e(TAG, "Failed to execute unregisterService " + e);
728 return false;
729 }
730 return true;
731 }
732
733 private boolean updateService(int regId, DnsSdTxtRecord t) {
734 if (DBG) Slog.d(TAG, "updateService: " + regId + " " + t);
735 try {
736 if (t == null) return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700737 mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData());
738 } catch(NativeDaemonConnectorException e) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700739 Slog.e(TAG, "Failed to updateServices " + e);
740 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700741 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700742 return true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700743 }
744
Irfan Sheriff817388e2012-04-11 14:52:19 -0700745 private boolean discoverServices(int discoveryId, String serviceType) {
746 if (DBG) Slog.d(TAG, "discoverServices: " + discoveryId + " " + serviceType);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700747 try {
748 mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType);
749 } catch(NativeDaemonConnectorException e) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700750 Slog.e(TAG, "Failed to discoverServices " + e);
751 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700752 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700753 return true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700754 }
755
Irfan Sheriff817388e2012-04-11 14:52:19 -0700756 private boolean stopServiceDiscovery(int discoveryId) {
757 if (DBG) Slog.d(TAG, "stopServiceDiscovery: " + discoveryId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700758 try {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700759 mNativeConnector.execute("mdnssd", "stop-discover", discoveryId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700760 } catch(NativeDaemonConnectorException e) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700761 Slog.e(TAG, "Failed to stopServiceDiscovery " + e);
762 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700763 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700764 return true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700765 }
766
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700767 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700768 if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700769 try {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700770 mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(),
771 service.getServiceType(), "local.");
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700772 } catch(NativeDaemonConnectorException e) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700773 Slog.e(TAG, "Failed to resolveService " + e);
774 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700775 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700776 return true;
777 }
778
779 private boolean stopResolveService(int resolveId) {
780 if (DBG) Slog.d(TAG, "stopResolveService: " + resolveId);
781 try {
782 mNativeConnector.execute("mdnssd", "stop-resolve", resolveId);
783 } catch(NativeDaemonConnectorException e) {
784 Slog.e(TAG, "Failed to stop resolve " + e);
785 return false;
786 }
787 return true;
788 }
789
790 private boolean getAddrInfo(int resolveId, String hostname) {
791 if (DBG) Slog.d(TAG, "getAdddrInfo: " + resolveId);
792 try {
793 mNativeConnector.execute("mdnssd", "getaddrinfo", resolveId, hostname);
794 } catch(NativeDaemonConnectorException e) {
795 Slog.e(TAG, "Failed to getAddrInfo " + e);
796 return false;
797 }
798 return true;
799 }
800
801 private boolean stopGetAddrInfo(int resolveId) {
802 if (DBG) Slog.d(TAG, "stopGetAdddrInfo: " + resolveId);
803 try {
804 mNativeConnector.execute("mdnssd", "stop-getaddrinfo", resolveId);
805 } catch(NativeDaemonConnectorException e) {
806 Slog.e(TAG, "Failed to stopGetAddrInfo " + e);
807 return false;
808 }
809 return true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700810 }
811
812 @Override
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700813 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700814 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
815 != PackageManager.PERMISSION_GRANTED) {
816 pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid="
817 + Binder.getCallingPid()
818 + ", uid=" + Binder.getCallingUid());
819 return;
820 }
821
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700822 for (ClientInfo client : mClients.values()) {
823 pw.println("Client Info");
824 pw.println(client);
825 }
826
827 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700828 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700829
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700830 /* arg2 on the source message has an id that needs to be retained in replies
831 * see NsdManager for details */
832 private Message obtainMessage(Message srcMsg) {
833 Message msg = Message.obtain();
834 msg.arg2 = srcMsg.arg2;
835 return msg;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700836 }
837
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700838 private void replyToMessage(Message msg, int what) {
839 if (msg.replyTo == null) return;
840 Message dstMsg = obtainMessage(msg);
841 dstMsg.what = what;
842 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700843 }
844
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700845 private void replyToMessage(Message msg, int what, int arg1) {
846 if (msg.replyTo == null) return;
847 Message dstMsg = obtainMessage(msg);
848 dstMsg.what = what;
849 dstMsg.arg1 = arg1;
850 mReplyChannel.replyToMessage(msg, dstMsg);
851 }
852
853 private void replyToMessage(Message msg, int what, Object obj) {
854 if (msg.replyTo == null) return;
855 Message dstMsg = obtainMessage(msg);
856 dstMsg.what = what;
857 dstMsg.obj = obj;
858 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700859 }
860
861 /* Information tracked per client */
862 private class ClientInfo {
863
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700864 private static final int MAX_LIMIT = 10;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700865 private final AsyncChannel mChannel;
866 private final Messenger mMessenger;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700867 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700868 private NsdServiceInfo mResolvedService;
869
870 /* A map from client id to unique id sent to mDns */
Hugo Benichi2183ba92017-04-05 14:06:11 +0900871 private final SparseArray<Integer> mClientIds = new SparseArray<>();
Irfan Sheriff817388e2012-04-11 14:52:19 -0700872
Dave Plattf31760c2014-03-07 14:48:22 -0800873 /* A map from client id to the type of the request we had received */
Hugo Benichi2183ba92017-04-05 14:06:11 +0900874 private final SparseArray<Integer> mClientRequests = new SparseArray<>();
Dave Plattf31760c2014-03-07 14:48:22 -0800875
Irfan Sheriff817388e2012-04-11 14:52:19 -0700876 private ClientInfo(AsyncChannel c, Messenger m) {
877 mChannel = c;
878 mMessenger = m;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700879 if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
880 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700881
882 @Override
883 public String toString() {
884 StringBuffer sb = new StringBuffer();
885 sb.append("mChannel ").append(mChannel).append("\n");
886 sb.append("mMessenger ").append(mMessenger).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700887 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700888 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattf31760c2014-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 Sheriff3ef889b2012-04-17 23:15:29 -0700893 }
894 return sb.toString();
895 }
Dave Plattf31760c2014-03-07 14:48:22 -0800896
897 // Remove any pending requests from the global map when we get rid of a client,
898 // and send cancellations to the daemon.
899 private void expungeAllRequests() {
900 int globalId, clientId, i;
901 for (i = 0; i < mClientIds.size(); i++) {
902 clientId = mClientIds.keyAt(i);
903 globalId = mClientIds.valueAt(i);
904 mIdToClientInfoMap.remove(globalId);
905 if (DBG) Slog.d(TAG, "Terminating client-ID " + clientId +
906 " global-ID " + globalId + " type " + mClientRequests.get(clientId));
907 switch (mClientRequests.get(clientId)) {
908 case NsdManager.DISCOVER_SERVICES:
909 stopServiceDiscovery(globalId);
910 break;
911 case NsdManager.RESOLVE_SERVICE:
912 stopResolveService(globalId);
913 break;
914 case NsdManager.REGISTER_SERVICE:
915 unregisterService(globalId);
916 break;
917 default:
918 break;
919 }
920 }
921 mClientIds.clear();
922 mClientRequests.clear();
923 }
924
Christopher Lane5a577902014-04-25 18:39:07 -0700925 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
926 // return the corresponding listener id. mDnsClient id is also called a global id.
927 private int getClientId(final int globalId) {
928 // This doesn't use mClientIds.indexOfValue because indexOfValue uses == (not .equals)
929 // while also coercing the int primitives to Integer objects.
930 for (int i = 0, nSize = mClientIds.size(); i < nSize; i++) {
931 int mDnsId = mClientIds.valueAt(i);
932 if (globalId == mDnsId) {
933 return mClientIds.keyAt(i);
934 }
935 }
936 return -1;
937 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700938 }
Hugo Benichicbb13672017-04-24 11:35:06 +0900939
940 @VisibleForTesting
941 public interface NsdSettings {
942 boolean isEnabled();
943 void putEnabledStatus(boolean isEnabled);
944
945 static NsdSettings makeDefault(Context context) {
946 ContentResolver resolver = context.getContentResolver();
947 return new NsdSettings() {
948 @Override
949 public boolean isEnabled() {
950 return Settings.Global.getInt(resolver, Settings.Global.NSD_ON, 1) == 1;
951 }
952
953 @Override
954 public void putEnabledStatus(boolean isEnabled) {
955 Settings.Global.putInt(resolver, Settings.Global.NSD_ON, isEnabled ? 1 : 0);
956 }
957 };
958 }
959 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700960}