blob: 3efef017d24f2950951205dfc098da5b3dfc79e9 [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;
Hugo Benichi03f6e1d2017-05-25 13:49:32 +090024import android.net.Uri;
Irfan Sheriff22af38c2012-05-03 16:44:27 -070025import android.net.nsd.NsdServiceInfo;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070026import android.net.nsd.DnsSdTxtRecord;
27import android.net.nsd.INsdManager;
28import android.net.nsd.NsdManager;
29import android.os.Binder;
Hugo Benichicbb13672017-04-24 11:35:06 +090030import android.os.HandlerThread;
31import android.os.Handler;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070032import android.os.Message;
33import android.os.Messenger;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070034import android.os.UserHandle;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070035import android.provider.Settings;
Philip P. Moltmann312c61e2016-03-16 10:15:39 -070036import android.util.Base64;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070037import android.util.Slog;
Irfan Sheriff22af38c2012-05-03 16:44:27 -070038import android.util.SparseArray;
Hugo Benichi0f86b442017-04-11 14:42:47 +090039import android.util.SparseIntArray;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070040
41import java.io.FileDescriptor;
42import java.io.PrintWriter;
Irfan Sheriff817388e2012-04-11 14:52:19 -070043import java.net.InetAddress;
Hugo Benichi1fac3192017-04-24 16:19:58 +090044import java.util.Arrays;
Irfan Sheriff817388e2012-04-11 14:52:19 -070045import java.util.HashMap;
Irfan Sheriff817388e2012-04-11 14:52:19 -070046import java.util.concurrent.CountDownLatch;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070047
Hugo Benichicbb13672017-04-24 11:35:06 +090048import com.android.internal.annotations.VisibleForTesting;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070049import com.android.internal.util.AsyncChannel;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060050import com.android.internal.util.DumpUtils;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070051import com.android.internal.util.Protocol;
52import com.android.internal.util.State;
53import com.android.internal.util.StateMachine;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070054
55/**
56 * Network Service Discovery Service handles remote service discovery operation requests by
57 * implementing the INsdManager interface.
58 *
59 * @hide
60 */
61public class NsdService extends INsdManager.Stub {
62 private static final String TAG = "NsdService";
63 private static final String MDNS_TAG = "mDnsConnector";
64
Hugo Benichi2183ba92017-04-05 14:06:11 +090065 private static final boolean DBG = true;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070066
Hugo Benichi2183ba92017-04-05 14:06:11 +090067 private final Context mContext;
Hugo Benichicbb13672017-04-24 11:35:06 +090068 private final NsdSettings mNsdSettings;
Hugo Benichi2183ba92017-04-05 14:06:11 +090069 private final NsdStateMachine mNsdStateMachine;
Hugo Benichi1fac3192017-04-24 16:19:58 +090070 private final DaemonConnection mDaemon;
71 private final NativeCallbackReceiver mDaemonCallback;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070072
73 /**
74 * Clients receiving asynchronous messages
75 */
Hugo Benichi2183ba92017-04-05 14:06:11 +090076 private final HashMap<Messenger, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff7d024d32012-03-22 17:01:39 -070077
Irfan Sheriff22af38c2012-05-03 16:44:27 -070078 /* A map from unique id to client info */
Hugo Benichi2183ba92017-04-05 14:06:11 +090079 private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
Irfan Sheriff22af38c2012-05-03 16:44:27 -070080
Hugo Benichi2183ba92017-04-05 14:06:11 +090081 private final AsyncChannel mReplyChannel = new AsyncChannel();
Irfan Sheriff7d024d32012-03-22 17:01:39 -070082
Hugo Benichi2183ba92017-04-05 14:06:11 +090083 private static final int INVALID_ID = 0;
Irfan Sheriff817388e2012-04-11 14:52:19 -070084 private int mUniqueId = 1;
85
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070086 private class NsdStateMachine extends StateMachine {
87
Irfan Sheriff22af38c2012-05-03 16:44:27 -070088 private final DefaultState mDefaultState = new DefaultState();
89 private final DisabledState mDisabledState = new DisabledState();
90 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070091
92 @Override
Wink Savillebbf30dfd2012-05-29 12:40:46 -070093 protected String getWhatToString(int what) {
Hugo Benichi2183ba92017-04-05 14:06:11 +090094 return NsdManager.nameOf(what);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070095 }
96
Irfan Sheriff919aca52012-06-01 16:44:13 -070097 /**
98 * Observes the NSD on/off setting, and takes action when changed.
99 */
100 private void registerForNsdSetting() {
Hugo Benichi03f6e1d2017-05-25 13:49:32 +0900101 final ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
Irfan Sheriff919aca52012-06-01 16:44:13 -0700102 @Override
Hugo Benichi2183ba92017-04-05 14:06:11 +0900103 public void onChange(boolean selfChange) {
Hugo Benichi2d968b42017-04-24 16:41:03 +0900104 notifyEnabled(isNsdEnabled());
Hugo Benichi2183ba92017-04-05 14:06:11 +0900105 }
Irfan Sheriff919aca52012-06-01 16:44:13 -0700106 };
107
Hugo Benichi03f6e1d2017-05-25 13:49:32 +0900108 final Uri uri = Settings.Global.getUriFor(Settings.Global.NSD_ON);
109 mNsdSettings.registerContentObserver(uri, contentObserver);
Irfan Sheriff919aca52012-06-01 16:44:13 -0700110 }
111
Hugo Benichicbb13672017-04-24 11:35:06 +0900112 NsdStateMachine(String name, Handler handler) {
113 super(name, handler);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700114 addState(mDefaultState);
115 addState(mDisabledState, mDefaultState);
116 addState(mEnabledState, mDefaultState);
Hugo Benichi2d968b42017-04-24 16:41:03 +0900117 State initialState = isNsdEnabled() ? mEnabledState : mDisabledState;
118 setInitialState(initialState);
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700119 setLogRecSize(25);
Irfan Sheriff919aca52012-06-01 16:44:13 -0700120 registerForNsdSetting();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700121 }
122
123 class DefaultState extends State {
124 @Override
125 public boolean processMessage(Message msg) {
Dave Plattf31760c2014-03-07 14:48:22 -0800126 ClientInfo cInfo = null;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700127 switch (msg.what) {
128 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
129 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
130 AsyncChannel c = (AsyncChannel) msg.obj;
131 if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
132 c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
Dave Plattf31760c2014-03-07 14:48:22 -0800133 cInfo = new ClientInfo(c, msg.replyTo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700134 mClients.put(msg.replyTo, cInfo);
135 } else {
136 Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
137 }
138 break;
139 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Dave Plattf31760c2014-03-07 14:48:22 -0800140 switch (msg.arg1) {
141 case AsyncChannel.STATUS_SEND_UNSUCCESSFUL:
142 Slog.e(TAG, "Send failed, client connection lost");
143 break;
144 case AsyncChannel.STATUS_REMOTE_DISCONNECTION:
145 if (DBG) Slog.d(TAG, "Client disconnected");
146 break;
147 default:
148 if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
149 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700150 }
Dave Plattf31760c2014-03-07 14:48:22 -0800151 cInfo = mClients.get(msg.replyTo);
152 if (cInfo != null) {
153 cInfo.expungeAllRequests();
154 mClients.remove(msg.replyTo);
155 }
156 //Last client
157 if (mClients.size() == 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900158 mDaemon.stop();
Dave Plattf31760c2014-03-07 14:48:22 -0800159 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700160 break;
161 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
162 AsyncChannel ac = new AsyncChannel();
163 ac.connect(mContext, getHandler(), msg.replyTo);
164 break;
165 case NsdManager.DISCOVER_SERVICES:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700166 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
167 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700168 break;
169 case NsdManager.STOP_DISCOVERY:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700170 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
171 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700172 break;
173 case NsdManager.REGISTER_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700174 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
175 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700176 break;
177 case NsdManager.UNREGISTER_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700178 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
179 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700180 break;
181 case NsdManager.RESOLVE_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700182 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
183 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700184 break;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700185 case NsdManager.NATIVE_DAEMON_EVENT:
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700186 default:
187 Slog.e(TAG, "Unhandled " + msg);
188 return NOT_HANDLED;
189 }
190 return HANDLED;
191 }
192 }
193
194 class DisabledState extends State {
195 @Override
196 public void enter() {
197 sendNsdStateChangeBroadcast(false);
198 }
199
200 @Override
201 public boolean processMessage(Message msg) {
202 switch (msg.what) {
203 case NsdManager.ENABLE:
204 transitionTo(mEnabledState);
205 break;
206 default:
207 return NOT_HANDLED;
208 }
209 return HANDLED;
210 }
211 }
212
213 class EnabledState extends State {
214 @Override
215 public void enter() {
216 sendNsdStateChangeBroadcast(true);
217 if (mClients.size() > 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900218 mDaemon.start();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700219 }
220 }
221
222 @Override
223 public void exit() {
224 if (mClients.size() > 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900225 mDaemon.stop();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700226 }
227 }
228
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700229 private boolean requestLimitReached(ClientInfo clientInfo) {
230 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
231 if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
232 return true;
233 }
234 return false;
235 }
236
Dave Plattf31760c2014-03-07 14:48:22 -0800237 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700238 clientInfo.mClientIds.put(clientId, globalId);
Dave Plattf31760c2014-03-07 14:48:22 -0800239 clientInfo.mClientRequests.put(clientId, what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700240 mIdToClientInfoMap.put(globalId, clientInfo);
241 }
242
243 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900244 clientInfo.mClientIds.delete(clientId);
245 clientInfo.mClientRequests.delete(clientId);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700246 mIdToClientInfoMap.remove(globalId);
247 }
248
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700249 @Override
250 public boolean processMessage(Message msg) {
251 ClientInfo clientInfo;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700252 NsdServiceInfo servInfo;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700253 int id;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700254 switch (msg.what) {
Hugo Benichi23dba852017-04-05 14:43:29 +0900255 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700256 //First client
257 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL &&
258 mClients.size() == 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900259 mDaemon.start();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700260 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900261 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700262 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Hugo Benichi23dba852017-04-05 14:43:29 +0900263 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700264 case NsdManager.DISABLE:
265 //TODO: cleanup clients
266 transitionTo(mDisabledState);
267 break;
268 case NsdManager.DISCOVER_SERVICES:
269 if (DBG) Slog.d(TAG, "Discover services");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700270 servInfo = (NsdServiceInfo) msg.obj;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700271 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700272
273 if (requestLimitReached(clientInfo)) {
274 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
275 NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700276 break;
277 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700278
279 id = getUniqueId();
280 if (discoverServices(id, servInfo.getServiceType())) {
281 if (DBG) {
282 Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
283 servInfo.getServiceType());
284 }
Dave Plattf31760c2014-03-07 14:48:22 -0800285 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700286 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700287 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700288 stopServiceDiscovery(id);
289 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
290 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700291 }
292 break;
293 case NsdManager.STOP_DISCOVERY:
294 if (DBG) Slog.d(TAG, "Stop service discovery");
295 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700296
297 try {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900298 id = clientInfo.mClientIds.get(msg.arg2);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700299 } catch (NullPointerException e) {
300 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
301 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700302 break;
303 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700304 removeRequestMap(msg.arg2, id, clientInfo);
305 if (stopServiceDiscovery(id)) {
306 replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700307 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700308 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
309 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700310 }
311 break;
312 case NsdManager.REGISTER_SERVICE:
313 if (DBG) Slog.d(TAG, "Register service");
314 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700315 if (requestLimitReached(clientInfo)) {
316 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
317 NsdManager.FAILURE_MAX_LIMIT);
318 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700319 }
320
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700321 id = getUniqueId();
322 if (registerService(id, (NsdServiceInfo) msg.obj)) {
323 if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
Dave Plattf31760c2014-03-07 14:48:22 -0800324 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700325 // Return success after mDns reports success
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700326 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700327 unregisterService(id);
328 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
329 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700330 }
331 break;
332 case NsdManager.UNREGISTER_SERVICE:
333 if (DBG) Slog.d(TAG, "unregister service");
334 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700335 try {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900336 id = clientInfo.mClientIds.get(msg.arg2);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700337 } catch (NullPointerException e) {
338 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
339 NsdManager.FAILURE_INTERNAL_ERROR);
340 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700341 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700342 removeRequestMap(msg.arg2, id, clientInfo);
343 if (unregisterService(id)) {
344 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
345 } else {
346 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
347 NsdManager.FAILURE_INTERNAL_ERROR);
348 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700349 break;
350 case NsdManager.RESOLVE_SERVICE:
351 if (DBG) Slog.d(TAG, "Resolve service");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700352 servInfo = (NsdServiceInfo) msg.obj;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700353 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700354
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700355
356 if (clientInfo.mResolvedService != null) {
357 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
358 NsdManager.FAILURE_ALREADY_ACTIVE);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700359 break;
360 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700361
362 id = getUniqueId();
363 if (resolveService(id, servInfo)) {
364 clientInfo.mResolvedService = new NsdServiceInfo();
Dave Plattf31760c2014-03-07 14:48:22 -0800365 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700366 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700367 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
368 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700369 }
370 break;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700371 case NsdManager.NATIVE_DAEMON_EVENT:
372 NativeEvent event = (NativeEvent) msg.obj;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700373 if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
Hugo Benichi23dba852017-04-05 14:43:29 +0900374 return NOT_HANDLED;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700375 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700376 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700377 default:
Hugo Benichi23dba852017-04-05 14:43:29 +0900378 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700379 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900380 return HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700381 }
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700382
383 private boolean handleNativeEvent(int code, String raw, String[] cooked) {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700384 NsdServiceInfo servInfo;
385 int id = Integer.parseInt(cooked[1]);
386 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
387 if (clientInfo == null) {
Hugo Benichi2183ba92017-04-05 14:06:11 +0900388 String name = NativeResponseCode.nameOf(code);
389 Slog.e(TAG, String.format("id %d for %s has no client mapping", id, name));
Hugo Benichi23dba852017-04-05 14:43:29 +0900390 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700391 }
392
393 /* This goes in response as msg.arg2 */
Christopher Lane5a577902014-04-25 18:39:07 -0700394 int clientId = clientInfo.getClientId(id);
395 if (clientId < 0) {
Vinit Deshapnde8ed09e82013-06-25 19:45:03 -0700396 // This can happen because of race conditions. For example,
397 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
398 // and we may get in this situation.
Hugo Benichi2183ba92017-04-05 14:06:11 +0900399 String name = NativeResponseCode.nameOf(code);
400 Slog.d(TAG, String.format(
401 "Notification %s for listener id %d that is no longer active",
402 name, id));
Hugo Benichi23dba852017-04-05 14:43:29 +0900403 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700404 }
Hugo Benichi2183ba92017-04-05 14:06:11 +0900405 if (DBG) {
406 String name = NativeResponseCode.nameOf(code);
407 Slog.d(TAG, String.format("Native daemon message %s: %s", name, raw));
408 }
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700409 switch (code) {
410 case NativeResponseCode.SERVICE_FOUND:
411 /* NNN uniqueId serviceName regType domain */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700412 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700413 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
414 clientId, servInfo);
415 break;
416 case NativeResponseCode.SERVICE_LOST:
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_LOST, 0,
420 clientId, servInfo);
421 break;
422 case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
423 /* NNN uniqueId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700424 clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
425 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
426 break;
427 case NativeResponseCode.SERVICE_REGISTERED:
428 /* NNN regId serviceName regType */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700429 servInfo = new NsdServiceInfo(cooked[2], null);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700430 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
431 id, clientId, servInfo);
432 break;
433 case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
434 /* NNN regId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700435 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
436 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
437 break;
438 case NativeResponseCode.SERVICE_UPDATED:
439 /* NNN regId */
440 break;
441 case NativeResponseCode.SERVICE_UPDATE_FAILED:
442 /* NNN regId errorCode */
443 break;
444 case NativeResponseCode.SERVICE_RESOLVED:
445 /* NNN resolveId fullName hostName port txtlen txtdata */
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700446 int index = 0;
447 while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
448 if (cooked[2].charAt(index) == '\\') {
449 ++index;
450 }
451 ++index;
452 }
453 if (index >= cooked[2].length()) {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700454 Slog.e(TAG, "Invalid service found " + raw);
455 break;
456 }
457 String name = cooked[2].substring(0, index);
458 String rest = cooked[2].substring(index);
459 String type = rest.replace(".local.", "");
460
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700461 name = unescape(name);
462
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700463 clientInfo.mResolvedService.setServiceName(name);
464 clientInfo.mResolvedService.setServiceType(type);
465 clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
Philip P. Moltmann312c61e2016-03-16 10:15:39 -0700466 clientInfo.mResolvedService.setTxtRecords(cooked[6]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700467
468 stopResolveService(id);
Vinit Deshapnde40387242013-11-12 15:36:37 -0800469 removeRequestMap(clientId, id, clientInfo);
470
471 int id2 = getUniqueId();
472 if (getAddrInfo(id2, cooked[3])) {
Dave Plattf31760c2014-03-07 14:48:22 -0800473 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
Vinit Deshapnde40387242013-11-12 15:36:37 -0800474 } else {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700475 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
476 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700477 clientInfo.mResolvedService = null;
478 }
479 break;
480 case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
481 /* NNN resolveId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700482 stopResolveService(id);
483 removeRequestMap(clientId, id, clientInfo);
484 clientInfo.mResolvedService = null;
485 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
486 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
487 break;
488 case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
489 /* NNN resolveId errorCode */
490 stopGetAddrInfo(id);
491 removeRequestMap(clientId, id, clientInfo);
492 clientInfo.mResolvedService = null;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700493 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
494 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
495 break;
496 case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
497 /* NNN resolveId hostname ttl addr */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700498 try {
499 clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
500 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
501 0, clientId, clientInfo.mResolvedService);
502 } catch (java.net.UnknownHostException e) {
503 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
504 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
505 }
506 stopGetAddrInfo(id);
507 removeRequestMap(clientId, id, clientInfo);
508 clientInfo.mResolvedService = null;
509 break;
510 default:
Hugo Benichi23dba852017-04-05 14:43:29 +0900511 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700512 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900513 return true;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700514 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700515 }
516 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700517
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700518 private String unescape(String s) {
519 StringBuilder sb = new StringBuilder(s.length());
520 for (int i = 0; i < s.length(); ++i) {
521 char c = s.charAt(i);
522 if (c == '\\') {
523 if (++i >= s.length()) {
524 Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
525 break;
526 }
527 c = s.charAt(i);
528 if (c != '.' && c != '\\') {
529 if (i + 2 >= s.length()) {
530 Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
531 break;
532 }
533 c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
534 i += 2;
535 }
536 }
537 sb.append(c);
538 }
539 return sb.toString();
540 }
541
Hugo Benichicbb13672017-04-24 11:35:06 +0900542 @VisibleForTesting
Hugo Benichi1fac3192017-04-24 16:19:58 +0900543 NsdService(Context ctx, NsdSettings settings, Handler handler, DaemonConnectionSupplier fn) {
Hugo Benichicbb13672017-04-24 11:35:06 +0900544 mContext = ctx;
545 mNsdSettings = settings;
Hugo Benichicbb13672017-04-24 11:35:06 +0900546 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700547 mNsdStateMachine.start();
Hugo Benichi1fac3192017-04-24 16:19:58 +0900548 mDaemonCallback = new NativeCallbackReceiver();
549 mDaemon = fn.get(mDaemonCallback);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700550 }
551
552 public static NsdService create(Context context) throws InterruptedException {
Hugo Benichicbb13672017-04-24 11:35:06 +0900553 NsdSettings settings = NsdSettings.makeDefault(context);
554 HandlerThread thread = new HandlerThread(TAG);
555 thread.start();
556 Handler handler = new Handler(thread.getLooper());
Hugo Benichi1fac3192017-04-24 16:19:58 +0900557 NsdService service = new NsdService(context, settings, handler, DaemonConnection::new);
558 service.mDaemonCallback.awaitConnection();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700559 return service;
560 }
561
562 public Messenger getMessenger() {
Hugo Benichicbb13672017-04-24 11:35:06 +0900563 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700564 return new Messenger(mNsdStateMachine.getHandler());
565 }
566
Hugo Benichi2d968b42017-04-24 16:41:03 +0900567 public void setEnabled(boolean isEnabled) {
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700568 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
569 "NsdService");
Hugo Benichi2d968b42017-04-24 16:41:03 +0900570 mNsdSettings.putEnabledStatus(isEnabled);
571 notifyEnabled(isEnabled);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700572 }
573
Hugo Benichi2d968b42017-04-24 16:41:03 +0900574 private void notifyEnabled(boolean isEnabled) {
575 mNsdStateMachine.sendMessage(isEnabled ? NsdManager.ENABLE : NsdManager.DISABLE);
576 }
577
578 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff54ac7a52012-04-19 10:26:34 -0700579 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700580 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi2d968b42017-04-24 16:41:03 +0900581 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
582 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700583 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700584 }
585
586 private boolean isNsdEnabled() {
Hugo Benichicbb13672017-04-24 11:35:06 +0900587 boolean ret = mNsdSettings.isEnabled();
588 if (DBG) {
589 Slog.d(TAG, "Network service discovery is " + (ret ? "enabled" : "disabled"));
590 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700591 return ret;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700592 }
593
Irfan Sheriff817388e2012-04-11 14:52:19 -0700594 private int getUniqueId() {
595 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
596 return mUniqueId;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700597 }
598
Sreeram Ramachandran03666c72014-07-19 23:21:46 -0700599 /* These should be in sync with system/netd/server/ResponseCode.h */
Hugo Benichi2183ba92017-04-05 14:06:11 +0900600 static final class NativeResponseCode {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700601 public static final int SERVICE_DISCOVERY_FAILED = 602;
602 public static final int SERVICE_FOUND = 603;
603 public static final int SERVICE_LOST = 604;
604
605 public static final int SERVICE_REGISTRATION_FAILED = 605;
606 public static final int SERVICE_REGISTERED = 606;
607
608 public static final int SERVICE_RESOLUTION_FAILED = 607;
609 public static final int SERVICE_RESOLVED = 608;
610
611 public static final int SERVICE_UPDATED = 609;
612 public static final int SERVICE_UPDATE_FAILED = 610;
613
614 public static final int SERVICE_GET_ADDR_FAILED = 611;
615 public static final int SERVICE_GET_ADDR_SUCCESS = 612;
Hugo Benichi2183ba92017-04-05 14:06:11 +0900616
617 private static final SparseArray<String> CODE_NAMES = new SparseArray<>();
618 static {
619 CODE_NAMES.put(SERVICE_DISCOVERY_FAILED, "SERVICE_DISCOVERY_FAILED");
620 CODE_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND");
621 CODE_NAMES.put(SERVICE_LOST, "SERVICE_LOST");
622 CODE_NAMES.put(SERVICE_REGISTRATION_FAILED, "SERVICE_REGISTRATION_FAILED");
623 CODE_NAMES.put(SERVICE_REGISTERED, "SERVICE_REGISTERED");
624 CODE_NAMES.put(SERVICE_RESOLUTION_FAILED, "SERVICE_RESOLUTION_FAILED");
625 CODE_NAMES.put(SERVICE_RESOLVED, "SERVICE_RESOLVED");
626 CODE_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
627 CODE_NAMES.put(SERVICE_UPDATE_FAILED, "SERVICE_UPDATE_FAILED");
628 CODE_NAMES.put(SERVICE_GET_ADDR_FAILED, "SERVICE_GET_ADDR_FAILED");
629 CODE_NAMES.put(SERVICE_GET_ADDR_SUCCESS, "SERVICE_GET_ADDR_SUCCESS");
630 }
631
632 static String nameOf(int code) {
633 String name = CODE_NAMES.get(code);
634 if (name == null) {
635 return Integer.toString(code);
636 }
637 return name;
638 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700639 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700640
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700641 private class NativeEvent {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700642 final int code;
643 final String raw;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700644 final String[] cooked;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700645
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700646 NativeEvent(int code, String raw, String[] cooked) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700647 this.code = code;
648 this.raw = raw;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700649 this.cooked = cooked;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700650 }
651 }
652
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700653 class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900654 private final CountDownLatch connected = new CountDownLatch(1);
655
656 public void awaitConnection() throws InterruptedException {
657 connected.await();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700658 }
659
Hugo Benichi1fac3192017-04-24 16:19:58 +0900660 @Override
661 public void onDaemonConnected() {
662 connected.countDown();
663 }
664
665 @Override
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800666 public boolean onCheckHoldWakeLock(int code) {
667 return false;
668 }
669
Hugo Benichi1fac3192017-04-24 16:19:58 +0900670 @Override
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700671 public boolean onEvent(int code, String raw, String[] cooked) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700672 // TODO: NDC translates a message to a callback, we could enhance NDC to
673 // directly interact with a state machine through messages
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700674 NativeEvent event = new NativeEvent(code, raw, cooked);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700675 mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
676 return true;
677 }
678 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700679
Hugo Benichi1fac3192017-04-24 16:19:58 +0900680 interface DaemonConnectionSupplier {
681 DaemonConnection get(NativeCallbackReceiver callback);
682 }
683
684 @VisibleForTesting
685 public static class DaemonConnection {
686 final NativeDaemonConnector mNativeConnector;
687
688 DaemonConnection(NativeCallbackReceiver callback) {
689 mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
690 new Thread(mNativeConnector, MDNS_TAG).start();
Irfan Sheriff817388e2012-04-11 14:52:19 -0700691 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900692
693 public boolean execute(Object... args) {
694 if (DBG) {
695 Slog.d(TAG, "mdnssd " + Arrays.toString(args));
696 }
697 try {
698 mNativeConnector.execute("mdnssd", args);
699 } catch (NativeDaemonConnectorException e) {
700 Slog.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
701 return false;
702 }
703 return true;
704 }
705
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900706 public void start() {
707 execute("start-service");
Hugo Benichi1fac3192017-04-24 16:19:58 +0900708 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900709
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900710 public void stop() {
711 execute("stop-service");
712 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700713 }
714
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700715 private boolean registerService(int regId, NsdServiceInfo service) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900716 if (DBG) {
717 Slog.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700718 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900719 String name = service.getServiceName();
720 String type = service.getServiceType();
721 int port = service.getPort();
722 byte[] textRecord = service.getTxtRecord();
723 String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900724 return mDaemon.execute("register", regId, name, type, port, record);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700725 }
726
Irfan Sheriff817388e2012-04-11 14:52:19 -0700727 private boolean unregisterService(int regId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900728 return mDaemon.execute("stop-register", regId);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700729 }
730
731 private boolean updateService(int regId, DnsSdTxtRecord t) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900732 if (t == null) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700733 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700734 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900735 return mDaemon.execute("update", regId, t.size(), t.getRawData());
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700736 }
737
Irfan Sheriff817388e2012-04-11 14:52:19 -0700738 private boolean discoverServices(int discoveryId, String serviceType) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900739 return mDaemon.execute("discover", discoveryId, serviceType);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700740 }
741
Irfan Sheriff817388e2012-04-11 14:52:19 -0700742 private boolean stopServiceDiscovery(int discoveryId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900743 return mDaemon.execute("stop-discover", discoveryId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700744 }
745
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700746 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900747 String name = service.getServiceName();
748 String type = service.getServiceType();
749 return mDaemon.execute("resolve", resolveId, name, type, "local.");
Irfan Sheriff817388e2012-04-11 14:52:19 -0700750 }
751
752 private boolean stopResolveService(int resolveId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900753 return mDaemon.execute("stop-resolve", resolveId);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700754 }
755
756 private boolean getAddrInfo(int resolveId, String hostname) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900757 return mDaemon.execute("getaddrinfo", resolveId, hostname);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700758 }
759
760 private boolean stopGetAddrInfo(int resolveId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900761 return mDaemon.execute("stop-getaddrinfo", resolveId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700762 }
763
764 @Override
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700765 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600766 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700767
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700768 for (ClientInfo client : mClients.values()) {
769 pw.println("Client Info");
770 pw.println(client);
771 }
772
773 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700774 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700775
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700776 /* arg2 on the source message has an id that needs to be retained in replies
777 * see NsdManager for details */
778 private Message obtainMessage(Message srcMsg) {
779 Message msg = Message.obtain();
780 msg.arg2 = srcMsg.arg2;
781 return msg;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700782 }
783
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700784 private void replyToMessage(Message msg, int what) {
785 if (msg.replyTo == null) return;
786 Message dstMsg = obtainMessage(msg);
787 dstMsg.what = what;
788 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700789 }
790
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700791 private void replyToMessage(Message msg, int what, int arg1) {
792 if (msg.replyTo == null) return;
793 Message dstMsg = obtainMessage(msg);
794 dstMsg.what = what;
795 dstMsg.arg1 = arg1;
796 mReplyChannel.replyToMessage(msg, dstMsg);
797 }
798
799 private void replyToMessage(Message msg, int what, Object obj) {
800 if (msg.replyTo == null) return;
801 Message dstMsg = obtainMessage(msg);
802 dstMsg.what = what;
803 dstMsg.obj = obj;
804 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700805 }
806
807 /* Information tracked per client */
808 private class ClientInfo {
809
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700810 private static final int MAX_LIMIT = 10;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700811 private final AsyncChannel mChannel;
812 private final Messenger mMessenger;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700813 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700814 private NsdServiceInfo mResolvedService;
815
816 /* A map from client id to unique id sent to mDns */
Hugo Benichi0f86b442017-04-11 14:42:47 +0900817 private final SparseIntArray mClientIds = new SparseIntArray();
Irfan Sheriff817388e2012-04-11 14:52:19 -0700818
Dave Plattf31760c2014-03-07 14:48:22 -0800819 /* A map from client id to the type of the request we had received */
Hugo Benichi0f86b442017-04-11 14:42:47 +0900820 private final SparseIntArray mClientRequests = new SparseIntArray();
Dave Plattf31760c2014-03-07 14:48:22 -0800821
Irfan Sheriff817388e2012-04-11 14:52:19 -0700822 private ClientInfo(AsyncChannel c, Messenger m) {
823 mChannel = c;
824 mMessenger = m;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700825 if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
826 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700827
828 @Override
829 public String toString() {
830 StringBuffer sb = new StringBuffer();
831 sb.append("mChannel ").append(mChannel).append("\n");
832 sb.append("mMessenger ").append(mMessenger).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700833 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700834 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattf31760c2014-03-07 14:48:22 -0800835 int clientID = mClientIds.keyAt(i);
836 sb.append("clientId ").append(clientID).
837 append(" mDnsId ").append(mClientIds.valueAt(i)).
838 append(" type ").append(mClientRequests.get(clientID)).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700839 }
840 return sb.toString();
841 }
Dave Plattf31760c2014-03-07 14:48:22 -0800842
843 // Remove any pending requests from the global map when we get rid of a client,
844 // and send cancellations to the daemon.
845 private void expungeAllRequests() {
846 int globalId, clientId, i;
Hugo Benichi0f86b442017-04-11 14:42:47 +0900847 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Dave Plattf31760c2014-03-07 14:48:22 -0800848 for (i = 0; i < mClientIds.size(); i++) {
849 clientId = mClientIds.keyAt(i);
850 globalId = mClientIds.valueAt(i);
851 mIdToClientInfoMap.remove(globalId);
852 if (DBG) Slog.d(TAG, "Terminating client-ID " + clientId +
853 " global-ID " + globalId + " type " + mClientRequests.get(clientId));
854 switch (mClientRequests.get(clientId)) {
855 case NsdManager.DISCOVER_SERVICES:
856 stopServiceDiscovery(globalId);
857 break;
858 case NsdManager.RESOLVE_SERVICE:
859 stopResolveService(globalId);
860 break;
861 case NsdManager.REGISTER_SERVICE:
862 unregisterService(globalId);
863 break;
864 default:
865 break;
866 }
867 }
868 mClientIds.clear();
869 mClientRequests.clear();
870 }
871
Christopher Lane5a577902014-04-25 18:39:07 -0700872 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
873 // return the corresponding listener id. mDnsClient id is also called a global id.
874 private int getClientId(final int globalId) {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900875 int idx = mClientIds.indexOfValue(globalId);
876 if (idx < 0) {
877 return idx;
Christopher Lane5a577902014-04-25 18:39:07 -0700878 }
Hugo Benichi0f86b442017-04-11 14:42:47 +0900879 return mClientIds.keyAt(idx);
Christopher Lane5a577902014-04-25 18:39:07 -0700880 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700881 }
Hugo Benichicbb13672017-04-24 11:35:06 +0900882
Hugo Benichi03f6e1d2017-05-25 13:49:32 +0900883 /**
884 * Interface which encapsulates dependencies of NsdService that are hard to mock, hard to
885 * override, or have side effects on global state in unit tests.
886 */
Hugo Benichicbb13672017-04-24 11:35:06 +0900887 @VisibleForTesting
888 public interface NsdSettings {
889 boolean isEnabled();
890 void putEnabledStatus(boolean isEnabled);
Hugo Benichi03f6e1d2017-05-25 13:49:32 +0900891 void registerContentObserver(Uri uri, ContentObserver observer);
Hugo Benichicbb13672017-04-24 11:35:06 +0900892
893 static NsdSettings makeDefault(Context context) {
Hugo Benichi03f6e1d2017-05-25 13:49:32 +0900894 final ContentResolver resolver = context.getContentResolver();
Hugo Benichicbb13672017-04-24 11:35:06 +0900895 return new NsdSettings() {
896 @Override
897 public boolean isEnabled() {
898 return Settings.Global.getInt(resolver, Settings.Global.NSD_ON, 1) == 1;
899 }
900
901 @Override
902 public void putEnabledStatus(boolean isEnabled) {
903 Settings.Global.putInt(resolver, Settings.Global.NSD_ON, isEnabled ? 1 : 0);
904 }
Hugo Benichi03f6e1d2017-05-25 13:49:32 +0900905
906 @Override
907 public void registerContentObserver(Uri uri, ContentObserver observer) {
908 resolver.registerContentObserver(uri, false, observer);
909 }
Hugo Benichicbb13672017-04-24 11:35:06 +0900910 };
911 }
912 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700913}