blob: 965835c4b21fd5c30459d4f6c6f4b20332e264e2 [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;
Hugo Benichi0f86b442017-04-11 14:42:47 +090038import android.util.SparseIntArray;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070039
40import java.io.FileDescriptor;
41import java.io.PrintWriter;
Irfan Sheriff817388e2012-04-11 14:52:19 -070042import java.net.InetAddress;
Hugo Benichi1fac3192017-04-24 16:19:58 +090043import java.util.Arrays;
Irfan Sheriff817388e2012-04-11 14:52:19 -070044import java.util.HashMap;
Irfan Sheriff817388e2012-04-11 14:52:19 -070045import java.util.concurrent.CountDownLatch;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070046
Hugo Benichicbb13672017-04-24 11:35:06 +090047import com.android.internal.annotations.VisibleForTesting;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070048import com.android.internal.util.AsyncChannel;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070049import com.android.internal.util.Protocol;
50import com.android.internal.util.State;
51import com.android.internal.util.StateMachine;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070052
53/**
54 * Network Service Discovery Service handles remote service discovery operation requests by
55 * implementing the INsdManager interface.
56 *
57 * @hide
58 */
59public class NsdService extends INsdManager.Stub {
60 private static final String TAG = "NsdService";
61 private static final String MDNS_TAG = "mDnsConnector";
62
Hugo Benichi2183ba92017-04-05 14:06:11 +090063 private static final boolean DBG = true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070064
Hugo Benichi2183ba92017-04-05 14:06:11 +090065 private final Context mContext;
Hugo Benichicbb13672017-04-24 11:35:06 +090066 private final NsdSettings mNsdSettings;
Hugo Benichi2183ba92017-04-05 14:06:11 +090067 private final NsdStateMachine mNsdStateMachine;
Hugo Benichi1fac3192017-04-24 16:19:58 +090068 private final DaemonConnection mDaemon;
69 private final NativeCallbackReceiver mDaemonCallback;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070070
71 /**
72 * Clients receiving asynchronous messages
73 */
Hugo Benichi2183ba92017-04-05 14:06:11 +090074 private final HashMap<Messenger, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff7d024d32012-03-22 17:01:39 -070075
Irfan Sheriff22af38c2012-05-03 16:44:27 -070076 /* A map from unique id to client info */
Hugo Benichi2183ba92017-04-05 14:06:11 +090077 private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
Irfan Sheriff22af38c2012-05-03 16:44:27 -070078
Hugo Benichi2183ba92017-04-05 14:06:11 +090079 private final AsyncChannel mReplyChannel = new AsyncChannel();
Irfan Sheriff7d024d32012-03-22 17:01:39 -070080
Hugo Benichi2183ba92017-04-05 14:06:11 +090081 private static final int INVALID_ID = 0;
Irfan Sheriff817388e2012-04-11 14:52:19 -070082 private int mUniqueId = 1;
83
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070084 private class NsdStateMachine extends StateMachine {
85
Irfan Sheriff22af38c2012-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 Sheriff3ef889b2012-04-17 23:15:29 -070089
90 @Override
Wink Savillebbf30dfd2012-05-29 12:40:46 -070091 protected String getWhatToString(int what) {
Hugo Benichi2183ba92017-04-05 14:06:11 +090092 return NsdManager.nameOf(what);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070093 }
94
Irfan Sheriff919aca52012-06-01 16:44:13 -070095 /**
96 * Observes the NSD on/off setting, and takes action when changed.
97 */
98 private void registerForNsdSetting() {
99 ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
100 @Override
Hugo Benichi2183ba92017-04-05 14:06:11 +0900101 public void onChange(boolean selfChange) {
102 if (isNsdEnabled()) {
103 mNsdStateMachine.sendMessage(NsdManager.ENABLE);
104 } else {
105 mNsdStateMachine.sendMessage(NsdManager.DISABLE);
Irfan Sheriff919aca52012-06-01 16:44:13 -0700106 }
Hugo Benichi2183ba92017-04-05 14:06:11 +0900107 }
Irfan Sheriff919aca52012-06-01 16:44:13 -0700108 };
109
110 mContext.getContentResolver().registerContentObserver(
Jeff Sharkey625239a2012-09-26 22:03:49 -0700111 Settings.Global.getUriFor(Settings.Global.NSD_ON),
Irfan Sheriff919aca52012-06-01 16:44:13 -0700112 false, contentObserver);
113 }
114
Hugo Benichicbb13672017-04-24 11:35:06 +0900115 NsdStateMachine(String name, Handler handler) {
116 super(name, handler);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700117 addState(mDefaultState);
118 addState(mDisabledState, mDefaultState);
119 addState(mEnabledState, mDefaultState);
120 if (isNsdEnabled()) {
121 setInitialState(mEnabledState);
122 } else {
123 setInitialState(mDisabledState);
124 }
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700125 setLogRecSize(25);
Irfan Sheriff919aca52012-06-01 16:44:13 -0700126 registerForNsdSetting();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700127 }
128
129 class DefaultState extends State {
130 @Override
131 public boolean processMessage(Message msg) {
Dave Plattf31760c2014-03-07 14:48:22 -0800132 ClientInfo cInfo = null;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700133 switch (msg.what) {
134 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
135 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
136 AsyncChannel c = (AsyncChannel) msg.obj;
137 if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
138 c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
Dave Plattf31760c2014-03-07 14:48:22 -0800139 cInfo = new ClientInfo(c, msg.replyTo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700140 mClients.put(msg.replyTo, cInfo);
141 } else {
142 Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
143 }
144 break;
145 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Dave Plattf31760c2014-03-07 14:48:22 -0800146 switch (msg.arg1) {
147 case AsyncChannel.STATUS_SEND_UNSUCCESSFUL:
148 Slog.e(TAG, "Send failed, client connection lost");
149 break;
150 case AsyncChannel.STATUS_REMOTE_DISCONNECTION:
151 if (DBG) Slog.d(TAG, "Client disconnected");
152 break;
153 default:
154 if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
155 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700156 }
Dave Plattf31760c2014-03-07 14:48:22 -0800157 cInfo = mClients.get(msg.replyTo);
158 if (cInfo != null) {
159 cInfo.expungeAllRequests();
160 mClients.remove(msg.replyTo);
161 }
162 //Last client
163 if (mClients.size() == 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900164 mDaemon.stop();
Dave Plattf31760c2014-03-07 14:48:22 -0800165 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700166 break;
167 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
168 AsyncChannel ac = new AsyncChannel();
169 ac.connect(mContext, getHandler(), msg.replyTo);
170 break;
171 case NsdManager.DISCOVER_SERVICES:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700172 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
173 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700174 break;
175 case NsdManager.STOP_DISCOVERY:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700176 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
177 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700178 break;
179 case NsdManager.REGISTER_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700180 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
181 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700182 break;
183 case NsdManager.UNREGISTER_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700184 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
185 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700186 break;
187 case NsdManager.RESOLVE_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700188 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
189 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700190 break;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700191 case NsdManager.NATIVE_DAEMON_EVENT:
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700192 default:
193 Slog.e(TAG, "Unhandled " + msg);
194 return NOT_HANDLED;
195 }
196 return HANDLED;
197 }
198 }
199
200 class DisabledState extends State {
201 @Override
202 public void enter() {
203 sendNsdStateChangeBroadcast(false);
204 }
205
206 @Override
207 public boolean processMessage(Message msg) {
208 switch (msg.what) {
209 case NsdManager.ENABLE:
210 transitionTo(mEnabledState);
211 break;
212 default:
213 return NOT_HANDLED;
214 }
215 return HANDLED;
216 }
217 }
218
219 class EnabledState extends State {
220 @Override
221 public void enter() {
222 sendNsdStateChangeBroadcast(true);
223 if (mClients.size() > 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900224 mDaemon.start();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700225 }
226 }
227
228 @Override
229 public void exit() {
230 if (mClients.size() > 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900231 mDaemon.stop();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700232 }
233 }
234
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700235 private boolean requestLimitReached(ClientInfo clientInfo) {
236 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
237 if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
238 return true;
239 }
240 return false;
241 }
242
Dave Plattf31760c2014-03-07 14:48:22 -0800243 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700244 clientInfo.mClientIds.put(clientId, globalId);
Dave Plattf31760c2014-03-07 14:48:22 -0800245 clientInfo.mClientRequests.put(clientId, what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700246 mIdToClientInfoMap.put(globalId, clientInfo);
247 }
248
249 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900250 clientInfo.mClientIds.delete(clientId);
251 clientInfo.mClientRequests.delete(clientId);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700252 mIdToClientInfoMap.remove(globalId);
253 }
254
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700255 @Override
256 public boolean processMessage(Message msg) {
257 ClientInfo clientInfo;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700258 NsdServiceInfo servInfo;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700259 int id;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700260 switch (msg.what) {
Hugo Benichi23dba852017-04-05 14:43:29 +0900261 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700262 //First client
263 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL &&
264 mClients.size() == 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900265 mDaemon.start();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700266 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900267 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700268 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Hugo Benichi23dba852017-04-05 14:43:29 +0900269 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700270 case NsdManager.DISABLE:
271 //TODO: cleanup clients
272 transitionTo(mDisabledState);
273 break;
274 case NsdManager.DISCOVER_SERVICES:
275 if (DBG) Slog.d(TAG, "Discover services");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700276 servInfo = (NsdServiceInfo) msg.obj;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700277 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700278
279 if (requestLimitReached(clientInfo)) {
280 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
281 NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700282 break;
283 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700284
285 id = getUniqueId();
286 if (discoverServices(id, servInfo.getServiceType())) {
287 if (DBG) {
288 Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
289 servInfo.getServiceType());
290 }
Dave Plattf31760c2014-03-07 14:48:22 -0800291 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700292 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700293 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700294 stopServiceDiscovery(id);
295 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
296 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700297 }
298 break;
299 case NsdManager.STOP_DISCOVERY:
300 if (DBG) Slog.d(TAG, "Stop service discovery");
301 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700302
303 try {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900304 id = clientInfo.mClientIds.get(msg.arg2);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700305 } catch (NullPointerException e) {
306 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
307 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700308 break;
309 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700310 removeRequestMap(msg.arg2, id, clientInfo);
311 if (stopServiceDiscovery(id)) {
312 replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700313 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700314 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
315 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700316 }
317 break;
318 case NsdManager.REGISTER_SERVICE:
319 if (DBG) Slog.d(TAG, "Register service");
320 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700321 if (requestLimitReached(clientInfo)) {
322 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
323 NsdManager.FAILURE_MAX_LIMIT);
324 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700325 }
326
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700327 id = getUniqueId();
328 if (registerService(id, (NsdServiceInfo) msg.obj)) {
329 if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
Dave Plattf31760c2014-03-07 14:48:22 -0800330 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700331 // Return success after mDns reports success
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700332 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700333 unregisterService(id);
334 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
335 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700336 }
337 break;
338 case NsdManager.UNREGISTER_SERVICE:
339 if (DBG) Slog.d(TAG, "unregister service");
340 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700341 try {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900342 id = clientInfo.mClientIds.get(msg.arg2);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700343 } catch (NullPointerException e) {
344 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
345 NsdManager.FAILURE_INTERNAL_ERROR);
346 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700347 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700348 removeRequestMap(msg.arg2, id, clientInfo);
349 if (unregisterService(id)) {
350 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
351 } else {
352 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
353 NsdManager.FAILURE_INTERNAL_ERROR);
354 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700355 break;
356 case NsdManager.RESOLVE_SERVICE:
357 if (DBG) Slog.d(TAG, "Resolve service");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700358 servInfo = (NsdServiceInfo) msg.obj;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700359 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700360
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700361
362 if (clientInfo.mResolvedService != null) {
363 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
364 NsdManager.FAILURE_ALREADY_ACTIVE);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700365 break;
366 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700367
368 id = getUniqueId();
369 if (resolveService(id, servInfo)) {
370 clientInfo.mResolvedService = new NsdServiceInfo();
Dave Plattf31760c2014-03-07 14:48:22 -0800371 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700372 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700373 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
374 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700375 }
376 break;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700377 case NsdManager.NATIVE_DAEMON_EVENT:
378 NativeEvent event = (NativeEvent) msg.obj;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700379 if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
Hugo Benichi23dba852017-04-05 14:43:29 +0900380 return NOT_HANDLED;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700381 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700382 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700383 default:
Hugo Benichi23dba852017-04-05 14:43:29 +0900384 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700385 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900386 return HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700387 }
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700388
389 private boolean handleNativeEvent(int code, String raw, String[] cooked) {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700390 NsdServiceInfo servInfo;
391 int id = Integer.parseInt(cooked[1]);
392 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
393 if (clientInfo == null) {
Hugo Benichi2183ba92017-04-05 14:06:11 +0900394 String name = NativeResponseCode.nameOf(code);
395 Slog.e(TAG, String.format("id %d for %s has no client mapping", id, name));
Hugo Benichi23dba852017-04-05 14:43:29 +0900396 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700397 }
398
399 /* This goes in response as msg.arg2 */
Christopher Lane5a577902014-04-25 18:39:07 -0700400 int clientId = clientInfo.getClientId(id);
401 if (clientId < 0) {
Vinit Deshapnde8ed09e82013-06-25 19:45:03 -0700402 // This can happen because of race conditions. For example,
403 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
404 // and we may get in this situation.
Hugo Benichi2183ba92017-04-05 14:06:11 +0900405 String name = NativeResponseCode.nameOf(code);
406 Slog.d(TAG, String.format(
407 "Notification %s for listener id %d that is no longer active",
408 name, id));
Hugo Benichi23dba852017-04-05 14:43:29 +0900409 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700410 }
Hugo Benichi2183ba92017-04-05 14:06:11 +0900411 if (DBG) {
412 String name = NativeResponseCode.nameOf(code);
413 Slog.d(TAG, String.format("Native daemon message %s: %s", name, raw));
414 }
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700415 switch (code) {
416 case NativeResponseCode.SERVICE_FOUND:
417 /* NNN uniqueId serviceName regType domain */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700418 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700419 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
420 clientId, servInfo);
421 break;
422 case NativeResponseCode.SERVICE_LOST:
423 /* NNN uniqueId serviceName regType domain */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700424 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700425 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
426 clientId, servInfo);
427 break;
428 case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
429 /* NNN uniqueId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700430 clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
431 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
432 break;
433 case NativeResponseCode.SERVICE_REGISTERED:
434 /* NNN regId serviceName regType */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700435 servInfo = new NsdServiceInfo(cooked[2], null);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700436 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
437 id, clientId, servInfo);
438 break;
439 case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
440 /* NNN regId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700441 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
442 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
443 break;
444 case NativeResponseCode.SERVICE_UPDATED:
445 /* NNN regId */
446 break;
447 case NativeResponseCode.SERVICE_UPDATE_FAILED:
448 /* NNN regId errorCode */
449 break;
450 case NativeResponseCode.SERVICE_RESOLVED:
451 /* NNN resolveId fullName hostName port txtlen txtdata */
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700452 int index = 0;
453 while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
454 if (cooked[2].charAt(index) == '\\') {
455 ++index;
456 }
457 ++index;
458 }
459 if (index >= cooked[2].length()) {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700460 Slog.e(TAG, "Invalid service found " + raw);
461 break;
462 }
463 String name = cooked[2].substring(0, index);
464 String rest = cooked[2].substring(index);
465 String type = rest.replace(".local.", "");
466
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700467 name = unescape(name);
468
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700469 clientInfo.mResolvedService.setServiceName(name);
470 clientInfo.mResolvedService.setServiceType(type);
471 clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
Philip P. Moltmann312c61e2016-03-16 10:15:39 -0700472 clientInfo.mResolvedService.setTxtRecords(cooked[6]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700473
474 stopResolveService(id);
Vinit Deshapnde40387242013-11-12 15:36:37 -0800475 removeRequestMap(clientId, id, clientInfo);
476
477 int id2 = getUniqueId();
478 if (getAddrInfo(id2, cooked[3])) {
Dave Plattf31760c2014-03-07 14:48:22 -0800479 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
Vinit Deshapnde40387242013-11-12 15:36:37 -0800480 } else {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700481 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
482 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700483 clientInfo.mResolvedService = null;
484 }
485 break;
486 case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
487 /* NNN resolveId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700488 stopResolveService(id);
489 removeRequestMap(clientId, id, clientInfo);
490 clientInfo.mResolvedService = null;
491 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
492 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
493 break;
494 case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
495 /* NNN resolveId errorCode */
496 stopGetAddrInfo(id);
497 removeRequestMap(clientId, id, clientInfo);
498 clientInfo.mResolvedService = null;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700499 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
500 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
501 break;
502 case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
503 /* NNN resolveId hostname ttl addr */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700504 try {
505 clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
506 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
507 0, clientId, clientInfo.mResolvedService);
508 } catch (java.net.UnknownHostException e) {
509 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
510 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
511 }
512 stopGetAddrInfo(id);
513 removeRequestMap(clientId, id, clientInfo);
514 clientInfo.mResolvedService = null;
515 break;
516 default:
Hugo Benichi23dba852017-04-05 14:43:29 +0900517 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700518 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900519 return true;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700520 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700521 }
522 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700523
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700524 private String unescape(String s) {
525 StringBuilder sb = new StringBuilder(s.length());
526 for (int i = 0; i < s.length(); ++i) {
527 char c = s.charAt(i);
528 if (c == '\\') {
529 if (++i >= s.length()) {
530 Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
531 break;
532 }
533 c = s.charAt(i);
534 if (c != '.' && c != '\\') {
535 if (i + 2 >= s.length()) {
536 Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
537 break;
538 }
539 c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
540 i += 2;
541 }
542 }
543 sb.append(c);
544 }
545 return sb.toString();
546 }
547
Hugo Benichicbb13672017-04-24 11:35:06 +0900548 @VisibleForTesting
Hugo Benichi1fac3192017-04-24 16:19:58 +0900549 NsdService(Context ctx, NsdSettings settings, Handler handler, DaemonConnectionSupplier fn) {
Hugo Benichicbb13672017-04-24 11:35:06 +0900550 mContext = ctx;
551 mNsdSettings = settings;
Hugo Benichicbb13672017-04-24 11:35:06 +0900552 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700553 mNsdStateMachine.start();
Hugo Benichi1fac3192017-04-24 16:19:58 +0900554 mDaemonCallback = new NativeCallbackReceiver();
555 mDaemon = fn.get(mDaemonCallback);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700556 }
557
558 public static NsdService create(Context context) throws InterruptedException {
Hugo Benichicbb13672017-04-24 11:35:06 +0900559 NsdSettings settings = NsdSettings.makeDefault(context);
560 HandlerThread thread = new HandlerThread(TAG);
561 thread.start();
562 Handler handler = new Handler(thread.getLooper());
Hugo Benichi1fac3192017-04-24 16:19:58 +0900563 NsdService service = new NsdService(context, settings, handler, DaemonConnection::new);
564 service.mDaemonCallback.awaitConnection();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700565 return service;
566 }
567
568 public Messenger getMessenger() {
Hugo Benichicbb13672017-04-24 11:35:06 +0900569 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700570 return new Messenger(mNsdStateMachine.getHandler());
571 }
572
573 public void setEnabled(boolean enable) {
574 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
575 "NsdService");
Hugo Benichicbb13672017-04-24 11:35:06 +0900576 mNsdSettings.putEnabledStatus(enable);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700577 if (enable) {
578 mNsdStateMachine.sendMessage(NsdManager.ENABLE);
579 } else {
580 mNsdStateMachine.sendMessage(NsdManager.DISABLE);
581 }
582 }
583
584 private void sendNsdStateChangeBroadcast(boolean enabled) {
Irfan Sheriff54ac7a52012-04-19 10:26:34 -0700585 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700586 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
587 if (enabled) {
588 intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_ENABLED);
589 } else {
590 intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED);
591 }
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700592 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700593 }
594
595 private boolean isNsdEnabled() {
Hugo Benichicbb13672017-04-24 11:35:06 +0900596 boolean ret = mNsdSettings.isEnabled();
597 if (DBG) {
598 Slog.d(TAG, "Network service discovery is " + (ret ? "enabled" : "disabled"));
599 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700600 return ret;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700601 }
602
Irfan Sheriff817388e2012-04-11 14:52:19 -0700603 private int getUniqueId() {
604 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
605 return mUniqueId;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700606 }
607
Sreeram Ramachandran03666c72014-07-19 23:21:46 -0700608 /* These should be in sync with system/netd/server/ResponseCode.h */
Hugo Benichi2183ba92017-04-05 14:06:11 +0900609 static final class NativeResponseCode {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700610 public static final int SERVICE_DISCOVERY_FAILED = 602;
611 public static final int SERVICE_FOUND = 603;
612 public static final int SERVICE_LOST = 604;
613
614 public static final int SERVICE_REGISTRATION_FAILED = 605;
615 public static final int SERVICE_REGISTERED = 606;
616
617 public static final int SERVICE_RESOLUTION_FAILED = 607;
618 public static final int SERVICE_RESOLVED = 608;
619
620 public static final int SERVICE_UPDATED = 609;
621 public static final int SERVICE_UPDATE_FAILED = 610;
622
623 public static final int SERVICE_GET_ADDR_FAILED = 611;
624 public static final int SERVICE_GET_ADDR_SUCCESS = 612;
Hugo Benichi2183ba92017-04-05 14:06:11 +0900625
626 private static final SparseArray<String> CODE_NAMES = new SparseArray<>();
627 static {
628 CODE_NAMES.put(SERVICE_DISCOVERY_FAILED, "SERVICE_DISCOVERY_FAILED");
629 CODE_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND");
630 CODE_NAMES.put(SERVICE_LOST, "SERVICE_LOST");
631 CODE_NAMES.put(SERVICE_REGISTRATION_FAILED, "SERVICE_REGISTRATION_FAILED");
632 CODE_NAMES.put(SERVICE_REGISTERED, "SERVICE_REGISTERED");
633 CODE_NAMES.put(SERVICE_RESOLUTION_FAILED, "SERVICE_RESOLUTION_FAILED");
634 CODE_NAMES.put(SERVICE_RESOLVED, "SERVICE_RESOLVED");
635 CODE_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
636 CODE_NAMES.put(SERVICE_UPDATE_FAILED, "SERVICE_UPDATE_FAILED");
637 CODE_NAMES.put(SERVICE_GET_ADDR_FAILED, "SERVICE_GET_ADDR_FAILED");
638 CODE_NAMES.put(SERVICE_GET_ADDR_SUCCESS, "SERVICE_GET_ADDR_SUCCESS");
639 }
640
641 static String nameOf(int code) {
642 String name = CODE_NAMES.get(code);
643 if (name == null) {
644 return Integer.toString(code);
645 }
646 return name;
647 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700648 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700649
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700650 private class NativeEvent {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700651 final int code;
652 final String raw;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700653 final String[] cooked;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700654
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700655 NativeEvent(int code, String raw, String[] cooked) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700656 this.code = code;
657 this.raw = raw;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700658 this.cooked = cooked;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700659 }
660 }
661
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700662 class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900663 private final CountDownLatch connected = new CountDownLatch(1);
664
665 public void awaitConnection() throws InterruptedException {
666 connected.await();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700667 }
668
Hugo Benichi1fac3192017-04-24 16:19:58 +0900669 @Override
670 public void onDaemonConnected() {
671 connected.countDown();
672 }
673
674 @Override
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800675 public boolean onCheckHoldWakeLock(int code) {
676 return false;
677 }
678
Hugo Benichi1fac3192017-04-24 16:19:58 +0900679 @Override
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700680 public boolean onEvent(int code, String raw, String[] cooked) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700681 // TODO: NDC translates a message to a callback, we could enhance NDC to
682 // directly interact with a state machine through messages
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700683 NativeEvent event = new NativeEvent(code, raw, cooked);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700684 mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
685 return true;
686 }
687 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700688
Hugo Benichi1fac3192017-04-24 16:19:58 +0900689 interface DaemonConnectionSupplier {
690 DaemonConnection get(NativeCallbackReceiver callback);
691 }
692
693 @VisibleForTesting
694 public static class DaemonConnection {
695 final NativeDaemonConnector mNativeConnector;
696
697 DaemonConnection(NativeCallbackReceiver callback) {
698 mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
699 new Thread(mNativeConnector, MDNS_TAG).start();
Irfan Sheriff817388e2012-04-11 14:52:19 -0700700 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900701
702 public boolean execute(Object... args) {
703 if (DBG) {
704 Slog.d(TAG, "mdnssd " + Arrays.toString(args));
705 }
706 try {
707 mNativeConnector.execute("mdnssd", args);
708 } catch (NativeDaemonConnectorException e) {
709 Slog.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
710 return false;
711 }
712 return true;
713 }
714
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900715 public void start() {
716 execute("start-service");
Hugo Benichi1fac3192017-04-24 16:19:58 +0900717 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900718
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900719 public void stop() {
720 execute("stop-service");
721 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700722 }
723
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700724 private boolean registerService(int regId, NsdServiceInfo service) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900725 if (DBG) {
726 Slog.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700727 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900728 String name = service.getServiceName();
729 String type = service.getServiceType();
730 int port = service.getPort();
731 byte[] textRecord = service.getTxtRecord();
732 String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900733 return mDaemon.execute("register", regId, name, type, port, record);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700734 }
735
Irfan Sheriff817388e2012-04-11 14:52:19 -0700736 private boolean unregisterService(int regId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900737 return mDaemon.execute("stop-register", regId);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700738 }
739
740 private boolean updateService(int regId, DnsSdTxtRecord t) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900741 if (t == null) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700742 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700743 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900744 return mDaemon.execute("update", regId, t.size(), t.getRawData());
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700745 }
746
Irfan Sheriff817388e2012-04-11 14:52:19 -0700747 private boolean discoverServices(int discoveryId, String serviceType) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900748 return mDaemon.execute("discover", discoveryId, serviceType);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700749 }
750
Irfan Sheriff817388e2012-04-11 14:52:19 -0700751 private boolean stopServiceDiscovery(int discoveryId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900752 return mDaemon.execute("stop-discover", discoveryId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700753 }
754
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700755 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900756 String name = service.getServiceName();
757 String type = service.getServiceType();
758 return mDaemon.execute("resolve", resolveId, name, type, "local.");
Irfan Sheriff817388e2012-04-11 14:52:19 -0700759 }
760
761 private boolean stopResolveService(int resolveId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900762 return mDaemon.execute("stop-resolve", resolveId);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700763 }
764
765 private boolean getAddrInfo(int resolveId, String hostname) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900766 return mDaemon.execute("getaddrinfo", resolveId, hostname);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700767 }
768
769 private boolean stopGetAddrInfo(int resolveId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900770 return mDaemon.execute("stop-getaddrinfo", resolveId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700771 }
772
773 @Override
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700774 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700775 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
776 != PackageManager.PERMISSION_GRANTED) {
777 pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid="
778 + Binder.getCallingPid()
779 + ", uid=" + Binder.getCallingUid());
780 return;
781 }
782
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700783 for (ClientInfo client : mClients.values()) {
784 pw.println("Client Info");
785 pw.println(client);
786 }
787
788 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700789 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700790
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700791 /* arg2 on the source message has an id that needs to be retained in replies
792 * see NsdManager for details */
793 private Message obtainMessage(Message srcMsg) {
794 Message msg = Message.obtain();
795 msg.arg2 = srcMsg.arg2;
796 return msg;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700797 }
798
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700799 private void replyToMessage(Message msg, int what) {
800 if (msg.replyTo == null) return;
801 Message dstMsg = obtainMessage(msg);
802 dstMsg.what = what;
803 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700804 }
805
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700806 private void replyToMessage(Message msg, int what, int arg1) {
807 if (msg.replyTo == null) return;
808 Message dstMsg = obtainMessage(msg);
809 dstMsg.what = what;
810 dstMsg.arg1 = arg1;
811 mReplyChannel.replyToMessage(msg, dstMsg);
812 }
813
814 private void replyToMessage(Message msg, int what, Object obj) {
815 if (msg.replyTo == null) return;
816 Message dstMsg = obtainMessage(msg);
817 dstMsg.what = what;
818 dstMsg.obj = obj;
819 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700820 }
821
822 /* Information tracked per client */
823 private class ClientInfo {
824
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700825 private static final int MAX_LIMIT = 10;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700826 private final AsyncChannel mChannel;
827 private final Messenger mMessenger;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700828 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700829 private NsdServiceInfo mResolvedService;
830
831 /* A map from client id to unique id sent to mDns */
Hugo Benichi0f86b442017-04-11 14:42:47 +0900832 private final SparseIntArray mClientIds = new SparseIntArray();
Irfan Sheriff817388e2012-04-11 14:52:19 -0700833
Dave Plattf31760c2014-03-07 14:48:22 -0800834 /* A map from client id to the type of the request we had received */
Hugo Benichi0f86b442017-04-11 14:42:47 +0900835 private final SparseIntArray mClientRequests = new SparseIntArray();
Dave Plattf31760c2014-03-07 14:48:22 -0800836
Irfan Sheriff817388e2012-04-11 14:52:19 -0700837 private ClientInfo(AsyncChannel c, Messenger m) {
838 mChannel = c;
839 mMessenger = m;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700840 if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
841 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700842
843 @Override
844 public String toString() {
845 StringBuffer sb = new StringBuffer();
846 sb.append("mChannel ").append(mChannel).append("\n");
847 sb.append("mMessenger ").append(mMessenger).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700848 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700849 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattf31760c2014-03-07 14:48:22 -0800850 int clientID = mClientIds.keyAt(i);
851 sb.append("clientId ").append(clientID).
852 append(" mDnsId ").append(mClientIds.valueAt(i)).
853 append(" type ").append(mClientRequests.get(clientID)).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700854 }
855 return sb.toString();
856 }
Dave Plattf31760c2014-03-07 14:48:22 -0800857
858 // Remove any pending requests from the global map when we get rid of a client,
859 // and send cancellations to the daemon.
860 private void expungeAllRequests() {
861 int globalId, clientId, i;
Hugo Benichi0f86b442017-04-11 14:42:47 +0900862 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Dave Plattf31760c2014-03-07 14:48:22 -0800863 for (i = 0; i < mClientIds.size(); i++) {
864 clientId = mClientIds.keyAt(i);
865 globalId = mClientIds.valueAt(i);
866 mIdToClientInfoMap.remove(globalId);
867 if (DBG) Slog.d(TAG, "Terminating client-ID " + clientId +
868 " global-ID " + globalId + " type " + mClientRequests.get(clientId));
869 switch (mClientRequests.get(clientId)) {
870 case NsdManager.DISCOVER_SERVICES:
871 stopServiceDiscovery(globalId);
872 break;
873 case NsdManager.RESOLVE_SERVICE:
874 stopResolveService(globalId);
875 break;
876 case NsdManager.REGISTER_SERVICE:
877 unregisterService(globalId);
878 break;
879 default:
880 break;
881 }
882 }
883 mClientIds.clear();
884 mClientRequests.clear();
885 }
886
Christopher Lane5a577902014-04-25 18:39:07 -0700887 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
888 // return the corresponding listener id. mDnsClient id is also called a global id.
889 private int getClientId(final int globalId) {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900890 int idx = mClientIds.indexOfValue(globalId);
891 if (idx < 0) {
892 return idx;
Christopher Lane5a577902014-04-25 18:39:07 -0700893 }
Hugo Benichi0f86b442017-04-11 14:42:47 +0900894 return mClientIds.keyAt(idx);
Christopher Lane5a577902014-04-25 18:39:07 -0700895 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700896 }
Hugo Benichicbb13672017-04-24 11:35:06 +0900897
898 @VisibleForTesting
899 public interface NsdSettings {
900 boolean isEnabled();
901 void putEnabledStatus(boolean isEnabled);
902
903 static NsdSettings makeDefault(Context context) {
904 ContentResolver resolver = context.getContentResolver();
905 return new NsdSettings() {
906 @Override
907 public boolean isEnabled() {
908 return Settings.Global.getInt(resolver, Settings.Global.NSD_ON, 1) == 1;
909 }
910
911 @Override
912 public void putEnabledStatus(boolean isEnabled) {
913 Settings.Global.putInt(resolver, Settings.Global.NSD_ON, isEnabled ? 1 : 0);
914 }
915 };
916 }
917 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700918}