blob: 16d2468e7c9d51f75865b249e9864ca5397673bc [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;
29import android.os.Handler;
30import android.os.HandlerThread;
31import android.os.Message;
32import android.os.Messenger;
33import android.os.IBinder;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070034import android.os.UserHandle;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070035import android.provider.Settings;
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 Sheriff7d024d32012-03-22 17:01:39 -070042import java.util.ArrayList;
Irfan Sheriff817388e2012-04-11 14:52:19 -070043import java.util.HashMap;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070044import java.util.List;
Irfan Sheriff817388e2012-04-11 14:52:19 -070045import java.util.concurrent.CountDownLatch;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070046
47import com.android.internal.app.IBatteryStats;
48import com.android.internal.telephony.TelephonyIntents;
49import com.android.internal.util.AsyncChannel;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070050import com.android.internal.util.Protocol;
51import com.android.internal.util.State;
52import com.android.internal.util.StateMachine;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070053import com.android.server.am.BatteryStatsService;
54import com.android.server.NativeDaemonConnector.Command;
55import com.android.internal.R;
56
57/**
58 * Network Service Discovery Service handles remote service discovery operation requests by
59 * implementing the INsdManager interface.
60 *
61 * @hide
62 */
63public class NsdService extends INsdManager.Stub {
64 private static final String TAG = "NsdService";
65 private static final String MDNS_TAG = "mDnsConnector";
66
67 private static final boolean DBG = true;
68
69 private Context mContext;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070070 private ContentResolver mContentResolver;
71 private NsdStateMachine mNsdStateMachine;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070072
73 /**
74 * Clients receiving asynchronous messages
75 */
Irfan Sheriff817388e2012-04-11 14:52:19 -070076 private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>();
Irfan Sheriff7d024d32012-03-22 17:01:39 -070077
Irfan Sheriff22af38c2012-05-03 16:44:27 -070078 /* A map from unique id to client info */
79 private SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<ClientInfo>();
80
Irfan Sheriff7d024d32012-03-22 17:01:39 -070081 private AsyncChannel mReplyChannel = new AsyncChannel();
82
Irfan Sheriff817388e2012-04-11 14:52:19 -070083 private int INVALID_ID = 0;
84 private int mUniqueId = 1;
85
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070086 private static final int BASE = Protocol.BASE_NSD_MANAGER;
Irfan Sheriff22af38c2012-05-03 16:44:27 -070087 private static final int CMD_TO_STRING_COUNT = NsdManager.RESOLVE_SERVICE - BASE + 1;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070088 private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
Irfan Sheriff7d024d32012-03-22 17:01:39 -070089
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070090 static {
91 sCmdToString[NsdManager.DISCOVER_SERVICES - BASE] = "DISCOVER";
92 sCmdToString[NsdManager.STOP_DISCOVERY - BASE] = "STOP-DISCOVER";
93 sCmdToString[NsdManager.REGISTER_SERVICE - BASE] = "REGISTER";
94 sCmdToString[NsdManager.UNREGISTER_SERVICE - BASE] = "UNREGISTER";
95 sCmdToString[NsdManager.RESOLVE_SERVICE - BASE] = "RESOLVE";
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070096 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -070097
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070098 private static String cmdToString(int cmd) {
99 cmd -= BASE;
100 if ((cmd >= 0) && (cmd < sCmdToString.length)) {
101 return sCmdToString[cmd];
102 } else {
103 return null;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700104 }
105 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700106
107 private class NsdStateMachine extends StateMachine {
108
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700109 private final DefaultState mDefaultState = new DefaultState();
110 private final DisabledState mDisabledState = new DisabledState();
111 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700112
113 @Override
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700114 protected String getWhatToString(int what) {
115 return cmdToString(what);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700116 }
117
Irfan Sheriff919aca52012-06-01 16:44:13 -0700118 /**
119 * Observes the NSD on/off setting, and takes action when changed.
120 */
121 private void registerForNsdSetting() {
122 ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
123 @Override
124 public void onChange(boolean selfChange) {
125 if (isNsdEnabled()) {
126 mNsdStateMachine.sendMessage(NsdManager.ENABLE);
127 } else {
128 mNsdStateMachine.sendMessage(NsdManager.DISABLE);
129 }
130 }
131 };
132
133 mContext.getContentResolver().registerContentObserver(
Jeff Sharkey625239a2012-09-26 22:03:49 -0700134 Settings.Global.getUriFor(Settings.Global.NSD_ON),
Irfan Sheriff919aca52012-06-01 16:44:13 -0700135 false, contentObserver);
136 }
137
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700138 NsdStateMachine(String name) {
139 super(name);
140 addState(mDefaultState);
141 addState(mDisabledState, mDefaultState);
142 addState(mEnabledState, mDefaultState);
143 if (isNsdEnabled()) {
144 setInitialState(mEnabledState);
145 } else {
146 setInitialState(mDisabledState);
147 }
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700148 setLogRecSize(25);
Irfan Sheriff919aca52012-06-01 16:44:13 -0700149 registerForNsdSetting();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700150 }
151
152 class DefaultState extends State {
153 @Override
154 public boolean processMessage(Message msg) {
155 switch (msg.what) {
156 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
157 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
158 AsyncChannel c = (AsyncChannel) msg.obj;
159 if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
160 c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
161 ClientInfo cInfo = new ClientInfo(c, msg.replyTo);
162 mClients.put(msg.replyTo, cInfo);
163 } else {
164 Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
165 }
166 break;
167 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
168 if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
169 Slog.e(TAG, "Send failed, client connection lost");
170 } else {
171 if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
172 }
173 mClients.remove(msg.replyTo);
174 break;
175 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
176 AsyncChannel ac = new AsyncChannel();
177 ac.connect(mContext, getHandler(), msg.replyTo);
178 break;
179 case NsdManager.DISCOVER_SERVICES:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700180 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
181 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700182 break;
183 case NsdManager.STOP_DISCOVERY:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700184 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
185 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700186 break;
187 case NsdManager.REGISTER_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700188 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
189 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700190 break;
191 case NsdManager.UNREGISTER_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700192 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
193 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700194 break;
195 case NsdManager.RESOLVE_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700196 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
197 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700198 break;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700199 case NsdManager.NATIVE_DAEMON_EVENT:
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700200 default:
201 Slog.e(TAG, "Unhandled " + msg);
202 return NOT_HANDLED;
203 }
204 return HANDLED;
205 }
206 }
207
208 class DisabledState extends State {
209 @Override
210 public void enter() {
211 sendNsdStateChangeBroadcast(false);
212 }
213
214 @Override
215 public boolean processMessage(Message msg) {
216 switch (msg.what) {
217 case NsdManager.ENABLE:
218 transitionTo(mEnabledState);
219 break;
220 default:
221 return NOT_HANDLED;
222 }
223 return HANDLED;
224 }
225 }
226
227 class EnabledState extends State {
228 @Override
229 public void enter() {
230 sendNsdStateChangeBroadcast(true);
231 if (mClients.size() > 0) {
232 startMDnsDaemon();
233 }
234 }
235
236 @Override
237 public void exit() {
238 if (mClients.size() > 0) {
239 stopMDnsDaemon();
240 }
241 }
242
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700243 private boolean requestLimitReached(ClientInfo clientInfo) {
244 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
245 if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
246 return true;
247 }
248 return false;
249 }
250
251 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
252 clientInfo.mClientIds.put(clientId, globalId);
253 mIdToClientInfoMap.put(globalId, clientInfo);
254 }
255
256 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
257 clientInfo.mClientIds.remove(clientId);
258 mIdToClientInfoMap.remove(globalId);
259 }
260
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700261 @Override
262 public boolean processMessage(Message msg) {
263 ClientInfo clientInfo;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700264 NsdServiceInfo servInfo;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700265 boolean result = HANDLED;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700266 int id;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700267 switch (msg.what) {
268 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
269 //First client
270 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL &&
271 mClients.size() == 0) {
272 startMDnsDaemon();
273 }
274 result = NOT_HANDLED;
275 break;
276 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
277 //Last client
278 if (mClients.size() == 1) {
279 stopMDnsDaemon();
280 }
281 result = NOT_HANDLED;
282 break;
283 case NsdManager.DISABLE:
284 //TODO: cleanup clients
285 transitionTo(mDisabledState);
286 break;
287 case NsdManager.DISCOVER_SERVICES:
288 if (DBG) Slog.d(TAG, "Discover services");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700289 servInfo = (NsdServiceInfo) msg.obj;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700290 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700291
292 if (requestLimitReached(clientInfo)) {
293 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
294 NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700295 break;
296 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700297
298 id = getUniqueId();
299 if (discoverServices(id, servInfo.getServiceType())) {
300 if (DBG) {
301 Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
302 servInfo.getServiceType());
303 }
304 storeRequestMap(msg.arg2, id, clientInfo);
305 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700306 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700307 stopServiceDiscovery(id);
308 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
309 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700310 }
311 break;
312 case NsdManager.STOP_DISCOVERY:
313 if (DBG) Slog.d(TAG, "Stop service discovery");
314 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700315
316 try {
317 id = clientInfo.mClientIds.get(msg.arg2).intValue();
318 } catch (NullPointerException e) {
319 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
320 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700321 break;
322 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700323 removeRequestMap(msg.arg2, id, clientInfo);
324 if (stopServiceDiscovery(id)) {
325 replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700326 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700327 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
328 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700329 }
330 break;
331 case NsdManager.REGISTER_SERVICE:
332 if (DBG) Slog.d(TAG, "Register service");
333 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700334 if (requestLimitReached(clientInfo)) {
335 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
336 NsdManager.FAILURE_MAX_LIMIT);
337 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700338 }
339
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700340 id = getUniqueId();
341 if (registerService(id, (NsdServiceInfo) msg.obj)) {
342 if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
343 storeRequestMap(msg.arg2, id, clientInfo);
344 // Return success after mDns reports success
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700345 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700346 unregisterService(id);
347 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
348 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700349 }
350 break;
351 case NsdManager.UNREGISTER_SERVICE:
352 if (DBG) Slog.d(TAG, "unregister service");
353 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700354 try {
355 id = clientInfo.mClientIds.get(msg.arg2).intValue();
356 } catch (NullPointerException e) {
357 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
358 NsdManager.FAILURE_INTERNAL_ERROR);
359 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700360 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700361 removeRequestMap(msg.arg2, id, clientInfo);
362 if (unregisterService(id)) {
363 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
364 } else {
365 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
366 NsdManager.FAILURE_INTERNAL_ERROR);
367 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700368 break;
369 case NsdManager.RESOLVE_SERVICE:
370 if (DBG) Slog.d(TAG, "Resolve service");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700371 servInfo = (NsdServiceInfo) msg.obj;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700372 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700373
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700374
375 if (clientInfo.mResolvedService != null) {
376 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
377 NsdManager.FAILURE_ALREADY_ACTIVE);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700378 break;
379 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700380
381 id = getUniqueId();
382 if (resolveService(id, servInfo)) {
383 clientInfo.mResolvedService = new NsdServiceInfo();
384 storeRequestMap(msg.arg2, id, clientInfo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700385 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700386 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
387 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700388 }
389 break;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700390 case NsdManager.NATIVE_DAEMON_EVENT:
391 NativeEvent event = (NativeEvent) msg.obj;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700392 if (!handleNativeEvent(event.code, event.raw,
393 NativeDaemonEvent.unescapeArgs(event.raw))) {
394 result = NOT_HANDLED;
395 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700396 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700397 default:
398 result = NOT_HANDLED;
399 break;
400 }
401 return result;
402 }
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700403
404 private boolean handleNativeEvent(int code, String raw, String[] cooked) {
405 boolean handled = true;
406 NsdServiceInfo servInfo;
407 int id = Integer.parseInt(cooked[1]);
408 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
409 if (clientInfo == null) {
410 Slog.e(TAG, "Unique id with no client mapping: " + id);
411 handled = false;
412 return handled;
413 }
414
415 /* This goes in response as msg.arg2 */
416 int clientId = -1;
417 int keyId = clientInfo.mClientIds.indexOfValue(id);
418 if (keyId != -1) {
419 clientId = clientInfo.mClientIds.keyAt(keyId);
Vinit Deshapnde8ed09e82013-06-25 19:45:03 -0700420 } else {
421 // This can happen because of race conditions. For example,
422 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
423 // and we may get in this situation.
424 Slog.d(TAG, "Notification for a listener that is no longer active: " + id);
425 handled = false;
426 return handled;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700427 }
Vinit Deshapnde8ed09e82013-06-25 19:45:03 -0700428
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700429 switch (code) {
430 case NativeResponseCode.SERVICE_FOUND:
431 /* NNN uniqueId serviceName regType domain */
432 if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw);
433 servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
434 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
435 clientId, servInfo);
436 break;
437 case NativeResponseCode.SERVICE_LOST:
438 /* NNN uniqueId serviceName regType domain */
439 if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw);
440 servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
441 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
442 clientId, servInfo);
443 break;
444 case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
445 /* NNN uniqueId errorCode */
446 if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw);
447 clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
448 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
449 break;
450 case NativeResponseCode.SERVICE_REGISTERED:
451 /* NNN regId serviceName regType */
452 if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw);
453 servInfo = new NsdServiceInfo(cooked[2], null, null);
454 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
455 id, clientId, servInfo);
456 break;
457 case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
458 /* NNN regId errorCode */
459 if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw);
460 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
461 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
462 break;
463 case NativeResponseCode.SERVICE_UPDATED:
464 /* NNN regId */
465 break;
466 case NativeResponseCode.SERVICE_UPDATE_FAILED:
467 /* NNN regId errorCode */
468 break;
469 case NativeResponseCode.SERVICE_RESOLVED:
470 /* NNN resolveId fullName hostName port txtlen txtdata */
471 if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
472 int index = cooked[2].indexOf(".");
473 if (index == -1) {
474 Slog.e(TAG, "Invalid service found " + raw);
475 break;
476 }
477 String name = cooked[2].substring(0, index);
478 String rest = cooked[2].substring(index);
479 String type = rest.replace(".local.", "");
480
481 clientInfo.mResolvedService.setServiceName(name);
482 clientInfo.mResolvedService.setServiceType(type);
483 clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
484
485 stopResolveService(id);
Vinit Deshapnde40387242013-11-12 15:36:37 -0800486 removeRequestMap(clientId, id, clientInfo);
487
488 int id2 = getUniqueId();
489 if (getAddrInfo(id2, cooked[3])) {
490 storeRequestMap(clientId, id2, clientInfo);
491 } else {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700492 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
493 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700494 clientInfo.mResolvedService = null;
495 }
496 break;
497 case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
498 /* NNN resolveId errorCode */
499 if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
500 stopResolveService(id);
501 removeRequestMap(clientId, id, clientInfo);
502 clientInfo.mResolvedService = null;
503 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
504 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
505 break;
506 case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
507 /* NNN resolveId errorCode */
508 stopGetAddrInfo(id);
509 removeRequestMap(clientId, id, clientInfo);
510 clientInfo.mResolvedService = null;
511 if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
512 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
513 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
514 break;
515 case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
516 /* NNN resolveId hostname ttl addr */
517 if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw);
518 try {
519 clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
520 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
521 0, clientId, clientInfo.mResolvedService);
522 } catch (java.net.UnknownHostException e) {
523 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
524 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
525 }
526 stopGetAddrInfo(id);
527 removeRequestMap(clientId, id, clientInfo);
528 clientInfo.mResolvedService = null;
529 break;
530 default:
531 handled = false;
532 break;
533 }
534 return handled;
535 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700536 }
537 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700538
539 private NativeDaemonConnector mNativeConnector;
540 private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1);
541
542 private NsdService(Context context) {
543 mContext = context;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700544 mContentResolver = context.getContentResolver();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700545
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700546 mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10,
547 MDNS_TAG, 25);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700548
549 mNsdStateMachine = new NsdStateMachine(TAG);
550 mNsdStateMachine.start();
551
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700552 Thread th = new Thread(mNativeConnector, MDNS_TAG);
553 th.start();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700554 }
555
556 public static NsdService create(Context context) throws InterruptedException {
557 NsdService service = new NsdService(context);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700558 service.mNativeDaemonConnected.await();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700559 return service;
560 }
561
562 public Messenger getMessenger() {
Irfan Sheriff92784672012-04-13 12:15:41 -0700563 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET,
564 "NsdService");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700565 return new Messenger(mNsdStateMachine.getHandler());
566 }
567
568 public void setEnabled(boolean enable) {
569 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
570 "NsdService");
Jeff Sharkey625239a2012-09-26 22:03:49 -0700571 Settings.Global.putInt(mContentResolver, Settings.Global.NSD_ON, enable ? 1 : 0);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700572 if (enable) {
573 mNsdStateMachine.sendMessage(NsdManager.ENABLE);
574 } else {
575 mNsdStateMachine.sendMessage(NsdManager.DISABLE);
576 }
577 }
578
579 private void sendNsdStateChangeBroadcast(boolean enabled) {
Irfan Sheriff54ac7a52012-04-19 10:26:34 -0700580 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700581 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
582 if (enabled) {
583 intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_ENABLED);
584 } else {
585 intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED);
586 }
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700587 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700588 }
589
590 private boolean isNsdEnabled() {
Jeff Sharkey625239a2012-09-26 22:03:49 -0700591 boolean ret = Settings.Global.getInt(mContentResolver, Settings.Global.NSD_ON, 1) == 1;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700592 if (DBG) Slog.d(TAG, "Network service discovery enabled " + ret);
593 return ret;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700594 }
595
Irfan Sheriff817388e2012-04-11 14:52:19 -0700596 private int getUniqueId() {
597 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
598 return mUniqueId;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700599 }
600
Irfan Sheriff817388e2012-04-11 14:52:19 -0700601 /* These should be in sync with system/netd/mDnsResponseCode.h */
602 class NativeResponseCode {
603 public static final int SERVICE_DISCOVERY_FAILED = 602;
604 public static final int SERVICE_FOUND = 603;
605 public static final int SERVICE_LOST = 604;
606
607 public static final int SERVICE_REGISTRATION_FAILED = 605;
608 public static final int SERVICE_REGISTERED = 606;
609
610 public static final int SERVICE_RESOLUTION_FAILED = 607;
611 public static final int SERVICE_RESOLVED = 608;
612
613 public static final int SERVICE_UPDATED = 609;
614 public static final int SERVICE_UPDATE_FAILED = 610;
615
616 public static final int SERVICE_GET_ADDR_FAILED = 611;
617 public static final int SERVICE_GET_ADDR_SUCCESS = 612;
618 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700619
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700620 private class NativeEvent {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700621 final int code;
622 final String raw;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700623
624 NativeEvent(int code, String raw) {
625 this.code = code;
626 this.raw = raw;
627 }
628 }
629
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700630 class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
631 public void onDaemonConnected() {
632 mNativeDaemonConnected.countDown();
633 }
634
635 public boolean onEvent(int code, String raw, String[] cooked) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700636 // TODO: NDC translates a message to a callback, we could enhance NDC to
637 // directly interact with a state machine through messages
638 NativeEvent event = new NativeEvent(code, raw);
639 mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
640 return true;
641 }
642 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700643
Irfan Sheriff817388e2012-04-11 14:52:19 -0700644 private boolean startMDnsDaemon() {
645 if (DBG) Slog.d(TAG, "startMDnsDaemon");
646 try {
647 mNativeConnector.execute("mdnssd", "start-service");
648 } catch(NativeDaemonConnectorException e) {
649 Slog.e(TAG, "Failed to start daemon" + e);
650 return false;
651 }
652 return true;
653 }
654
655 private boolean stopMDnsDaemon() {
656 if (DBG) Slog.d(TAG, "stopMDnsDaemon");
657 try {
658 mNativeConnector.execute("mdnssd", "stop-service");
659 } catch(NativeDaemonConnectorException e) {
660 Slog.e(TAG, "Failed to start daemon" + e);
661 return false;
662 }
663 return true;
664 }
665
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700666 private boolean registerService(int regId, NsdServiceInfo service) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700667 if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700668 try {
669 //Add txtlen and txtdata
670 mNativeConnector.execute("mdnssd", "register", regId, service.getServiceName(),
671 service.getServiceType(), service.getPort());
672 } catch(NativeDaemonConnectorException e) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700673 Slog.e(TAG, "Failed to execute registerService " + e);
674 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700675 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700676 return true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700677 }
678
Irfan Sheriff817388e2012-04-11 14:52:19 -0700679 private boolean unregisterService(int regId) {
680 if (DBG) Slog.d(TAG, "unregisterService: " + regId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700681 try {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700682 mNativeConnector.execute("mdnssd", "stop-register", regId);
683 } catch(NativeDaemonConnectorException e) {
684 Slog.e(TAG, "Failed to execute unregisterService " + e);
685 return false;
686 }
687 return true;
688 }
689
690 private boolean updateService(int regId, DnsSdTxtRecord t) {
691 if (DBG) Slog.d(TAG, "updateService: " + regId + " " + t);
692 try {
693 if (t == null) return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700694 mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData());
695 } catch(NativeDaemonConnectorException e) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700696 Slog.e(TAG, "Failed to updateServices " + e);
697 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700698 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700699 return true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700700 }
701
Irfan Sheriff817388e2012-04-11 14:52:19 -0700702 private boolean discoverServices(int discoveryId, String serviceType) {
703 if (DBG) Slog.d(TAG, "discoverServices: " + discoveryId + " " + serviceType);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700704 try {
705 mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType);
706 } catch(NativeDaemonConnectorException e) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700707 Slog.e(TAG, "Failed to discoverServices " + e);
708 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700709 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700710 return true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700711 }
712
Irfan Sheriff817388e2012-04-11 14:52:19 -0700713 private boolean stopServiceDiscovery(int discoveryId) {
714 if (DBG) Slog.d(TAG, "stopServiceDiscovery: " + discoveryId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700715 try {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700716 mNativeConnector.execute("mdnssd", "stop-discover", discoveryId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700717 } catch(NativeDaemonConnectorException e) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700718 Slog.e(TAG, "Failed to stopServiceDiscovery " + e);
719 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700720 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700721 return true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700722 }
723
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700724 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700725 if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700726 try {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700727 mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(),
728 service.getServiceType(), "local.");
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700729 } catch(NativeDaemonConnectorException e) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700730 Slog.e(TAG, "Failed to resolveService " + e);
731 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700732 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700733 return true;
734 }
735
736 private boolean stopResolveService(int resolveId) {
737 if (DBG) Slog.d(TAG, "stopResolveService: " + resolveId);
738 try {
739 mNativeConnector.execute("mdnssd", "stop-resolve", resolveId);
740 } catch(NativeDaemonConnectorException e) {
741 Slog.e(TAG, "Failed to stop resolve " + e);
742 return false;
743 }
744 return true;
745 }
746
747 private boolean getAddrInfo(int resolveId, String hostname) {
748 if (DBG) Slog.d(TAG, "getAdddrInfo: " + resolveId);
749 try {
750 mNativeConnector.execute("mdnssd", "getaddrinfo", resolveId, hostname);
751 } catch(NativeDaemonConnectorException e) {
752 Slog.e(TAG, "Failed to getAddrInfo " + e);
753 return false;
754 }
755 return true;
756 }
757
758 private boolean stopGetAddrInfo(int resolveId) {
759 if (DBG) Slog.d(TAG, "stopGetAdddrInfo: " + resolveId);
760 try {
761 mNativeConnector.execute("mdnssd", "stop-getaddrinfo", resolveId);
762 } catch(NativeDaemonConnectorException e) {
763 Slog.e(TAG, "Failed to stopGetAddrInfo " + e);
764 return false;
765 }
766 return true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700767 }
768
769 @Override
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700770 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700771 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
772 != PackageManager.PERMISSION_GRANTED) {
773 pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid="
774 + Binder.getCallingPid()
775 + ", uid=" + Binder.getCallingUid());
776 return;
777 }
778
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700779 for (ClientInfo client : mClients.values()) {
780 pw.println("Client Info");
781 pw.println(client);
782 }
783
784 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700785 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700786
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700787 /* arg2 on the source message has an id that needs to be retained in replies
788 * see NsdManager for details */
789 private Message obtainMessage(Message srcMsg) {
790 Message msg = Message.obtain();
791 msg.arg2 = srcMsg.arg2;
792 return msg;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700793 }
794
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700795 private void replyToMessage(Message msg, int what) {
796 if (msg.replyTo == null) return;
797 Message dstMsg = obtainMessage(msg);
798 dstMsg.what = what;
799 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700800 }
801
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700802 private void replyToMessage(Message msg, int what, int arg1) {
803 if (msg.replyTo == null) return;
804 Message dstMsg = obtainMessage(msg);
805 dstMsg.what = what;
806 dstMsg.arg1 = arg1;
807 mReplyChannel.replyToMessage(msg, dstMsg);
808 }
809
810 private void replyToMessage(Message msg, int what, Object obj) {
811 if (msg.replyTo == null) return;
812 Message dstMsg = obtainMessage(msg);
813 dstMsg.what = what;
814 dstMsg.obj = obj;
815 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700816 }
817
818 /* Information tracked per client */
819 private class ClientInfo {
820
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700821 private static final int MAX_LIMIT = 10;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700822 private final AsyncChannel mChannel;
823 private final Messenger mMessenger;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700824 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700825 private NsdServiceInfo mResolvedService;
826
827 /* A map from client id to unique id sent to mDns */
828 private SparseArray<Integer> mClientIds = new SparseArray<Integer>();
Irfan Sheriff817388e2012-04-11 14:52:19 -0700829
830 private ClientInfo(AsyncChannel c, Messenger m) {
831 mChannel = c;
832 mMessenger = m;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700833 if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
834 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700835
836 @Override
837 public String toString() {
838 StringBuffer sb = new StringBuffer();
839 sb.append("mChannel ").append(mChannel).append("\n");
840 sb.append("mMessenger ").append(mMessenger).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700841 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700842 for(int i = 0; i< mClientIds.size(); i++) {
843 sb.append("clientId ").append(mClientIds.keyAt(i));
844 sb.append(" mDnsId ").append(mClientIds.valueAt(i)).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700845 }
846 return sb.toString();
847 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700848 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700849}