blob: 55bca648d50dfd205690e2179b7818611403f925 [file] [log] [blame]
Irfan Sheriff7d024d32012-03-22 17:01:39 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.content.Context;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070020import android.content.ContentResolver;
21import android.content.Intent;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070022import android.content.pm.PackageManager;
Irfan Sheriff919aca52012-06-01 16:44:13 -070023import android.database.ContentObserver;
Irfan Sheriff22af38c2012-05-03 16:44:27 -070024import android.net.nsd.NsdServiceInfo;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070025import android.net.nsd.DnsSdTxtRecord;
26import android.net.nsd.INsdManager;
27import android.net.nsd.NsdManager;
28import android.os.Binder;
Hugo Benichicbb13672017-04-24 11:35:06 +090029import android.os.HandlerThread;
30import android.os.Handler;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070031import android.os.Message;
32import android.os.Messenger;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070033import android.os.UserHandle;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070034import android.provider.Settings;
Philip P. Moltmann312c61e2016-03-16 10:15:39 -070035import android.util.Base64;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070036import android.util.Slog;
Irfan Sheriff22af38c2012-05-03 16:44:27 -070037import android.util.SparseArray;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070038
39import java.io.FileDescriptor;
40import java.io.PrintWriter;
Irfan Sheriff817388e2012-04-11 14:52:19 -070041import java.net.InetAddress;
Hugo Benichi1fac3192017-04-24 16:19:58 +090042import java.util.Arrays;
Irfan Sheriff817388e2012-04-11 14:52:19 -070043import java.util.HashMap;
Irfan Sheriff817388e2012-04-11 14:52:19 -070044import java.util.concurrent.CountDownLatch;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070045
Hugo Benichicbb13672017-04-24 11:35:06 +090046import com.android.internal.annotations.VisibleForTesting;
Irfan Sheriff7d024d32012-03-22 17:01:39 -070047import com.android.internal.util.AsyncChannel;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -070048import com.android.internal.util.Protocol;
49import com.android.internal.util.State;
50import com.android.internal.util.StateMachine;
Christopher Lane771cd652014-04-14 16:31:27 -070051import com.android.server.NativeDaemonConnector.Command;
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) {
164 stopMDnsDaemon();
165 }
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) {
224 startMDnsDaemon();
225 }
226 }
227
228 @Override
229 public void exit() {
230 if (mClients.size() > 0) {
231 stopMDnsDaemon();
232 }
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) {
250 clientInfo.mClientIds.remove(clientId);
Dave Plattf31760c2014-03-07 14:48:22 -0800251 clientInfo.mClientRequests.remove(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) {
265 startMDnsDaemon();
266 }
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 {
304 id = clientInfo.mClientIds.get(msg.arg2).intValue();
305 } 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 {
342 id = clientInfo.mClientIds.get(msg.arg2).intValue();
343 } 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
715 public boolean execute(Command cmd) {
716 if (DBG) {
717 Slog.d(TAG, cmd.toString());
718 }
719 try {
720 mNativeConnector.execute(cmd);
721 } catch (NativeDaemonConnectorException e) {
722 Slog.e(TAG, "Failed to execute " + cmd, e);
723 return false;
724 }
725 return true;
726 }
727 }
728
729 private boolean startMDnsDaemon() {
730 return mDaemon.execute("start-service");
Irfan Sheriff817388e2012-04-11 14:52:19 -0700731 }
732
733 private boolean stopMDnsDaemon() {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900734 return mDaemon.execute("stop-service");
Irfan Sheriff817388e2012-04-11 14:52:19 -0700735 }
736
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700737 private boolean registerService(int regId, NsdServiceInfo service) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900738 if (DBG) {
739 Slog.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700740 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900741 String name = service.getServiceName();
742 String type = service.getServiceType();
743 int port = service.getPort();
744 byte[] textRecord = service.getTxtRecord();
745 String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
746 Command cmd = new Command("mdnssd", "register", regId, name, type, port, record);
747 return mDaemon.execute(cmd);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700748 }
749
Irfan Sheriff817388e2012-04-11 14:52:19 -0700750 private boolean unregisterService(int regId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900751 return mDaemon.execute("stop-register", regId);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700752 }
753
754 private boolean updateService(int regId, DnsSdTxtRecord t) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900755 if (t == null) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700756 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700757 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900758 return mDaemon.execute("update", regId, t.size(), t.getRawData());
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700759 }
760
Irfan Sheriff817388e2012-04-11 14:52:19 -0700761 private boolean discoverServices(int discoveryId, String serviceType) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900762 return mDaemon.execute("discover", discoveryId, serviceType);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700763 }
764
Irfan Sheriff817388e2012-04-11 14:52:19 -0700765 private boolean stopServiceDiscovery(int discoveryId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900766 return mDaemon.execute("stop-discover", discoveryId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700767 }
768
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700769 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900770 String name = service.getServiceName();
771 String type = service.getServiceType();
772 return mDaemon.execute("resolve", resolveId, name, type, "local.");
Irfan Sheriff817388e2012-04-11 14:52:19 -0700773 }
774
775 private boolean stopResolveService(int resolveId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900776 return mDaemon.execute("stop-resolve", resolveId);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700777 }
778
779 private boolean getAddrInfo(int resolveId, String hostname) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900780 return mDaemon.execute("getaddrinfo", resolveId, hostname);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700781 }
782
783 private boolean stopGetAddrInfo(int resolveId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900784 return mDaemon.execute("stop-getaddrinfo", resolveId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700785 }
786
787 @Override
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700788 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700789 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
790 != PackageManager.PERMISSION_GRANTED) {
791 pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid="
792 + Binder.getCallingPid()
793 + ", uid=" + Binder.getCallingUid());
794 return;
795 }
796
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700797 for (ClientInfo client : mClients.values()) {
798 pw.println("Client Info");
799 pw.println(client);
800 }
801
802 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700803 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700804
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700805 /* arg2 on the source message has an id that needs to be retained in replies
806 * see NsdManager for details */
807 private Message obtainMessage(Message srcMsg) {
808 Message msg = Message.obtain();
809 msg.arg2 = srcMsg.arg2;
810 return msg;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700811 }
812
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700813 private void replyToMessage(Message msg, int what) {
814 if (msg.replyTo == null) return;
815 Message dstMsg = obtainMessage(msg);
816 dstMsg.what = what;
817 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700818 }
819
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700820 private void replyToMessage(Message msg, int what, int arg1) {
821 if (msg.replyTo == null) return;
822 Message dstMsg = obtainMessage(msg);
823 dstMsg.what = what;
824 dstMsg.arg1 = arg1;
825 mReplyChannel.replyToMessage(msg, dstMsg);
826 }
827
828 private void replyToMessage(Message msg, int what, Object obj) {
829 if (msg.replyTo == null) return;
830 Message dstMsg = obtainMessage(msg);
831 dstMsg.what = what;
832 dstMsg.obj = obj;
833 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700834 }
835
836 /* Information tracked per client */
837 private class ClientInfo {
838
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700839 private static final int MAX_LIMIT = 10;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700840 private final AsyncChannel mChannel;
841 private final Messenger mMessenger;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700842 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700843 private NsdServiceInfo mResolvedService;
844
845 /* A map from client id to unique id sent to mDns */
Hugo Benichi2183ba92017-04-05 14:06:11 +0900846 private final SparseArray<Integer> mClientIds = new SparseArray<>();
Irfan Sheriff817388e2012-04-11 14:52:19 -0700847
Dave Plattf31760c2014-03-07 14:48:22 -0800848 /* A map from client id to the type of the request we had received */
Hugo Benichi2183ba92017-04-05 14:06:11 +0900849 private final SparseArray<Integer> mClientRequests = new SparseArray<>();
Dave Plattf31760c2014-03-07 14:48:22 -0800850
Irfan Sheriff817388e2012-04-11 14:52:19 -0700851 private ClientInfo(AsyncChannel c, Messenger m) {
852 mChannel = c;
853 mMessenger = m;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700854 if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
855 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700856
857 @Override
858 public String toString() {
859 StringBuffer sb = new StringBuffer();
860 sb.append("mChannel ").append(mChannel).append("\n");
861 sb.append("mMessenger ").append(mMessenger).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700862 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700863 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattf31760c2014-03-07 14:48:22 -0800864 int clientID = mClientIds.keyAt(i);
865 sb.append("clientId ").append(clientID).
866 append(" mDnsId ").append(mClientIds.valueAt(i)).
867 append(" type ").append(mClientRequests.get(clientID)).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700868 }
869 return sb.toString();
870 }
Dave Plattf31760c2014-03-07 14:48:22 -0800871
872 // Remove any pending requests from the global map when we get rid of a client,
873 // and send cancellations to the daemon.
874 private void expungeAllRequests() {
875 int globalId, clientId, i;
876 for (i = 0; i < mClientIds.size(); i++) {
877 clientId = mClientIds.keyAt(i);
878 globalId = mClientIds.valueAt(i);
879 mIdToClientInfoMap.remove(globalId);
880 if (DBG) Slog.d(TAG, "Terminating client-ID " + clientId +
881 " global-ID " + globalId + " type " + mClientRequests.get(clientId));
882 switch (mClientRequests.get(clientId)) {
883 case NsdManager.DISCOVER_SERVICES:
884 stopServiceDiscovery(globalId);
885 break;
886 case NsdManager.RESOLVE_SERVICE:
887 stopResolveService(globalId);
888 break;
889 case NsdManager.REGISTER_SERVICE:
890 unregisterService(globalId);
891 break;
892 default:
893 break;
894 }
895 }
896 mClientIds.clear();
897 mClientRequests.clear();
898 }
899
Christopher Lane5a577902014-04-25 18:39:07 -0700900 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
901 // return the corresponding listener id. mDnsClient id is also called a global id.
902 private int getClientId(final int globalId) {
903 // This doesn't use mClientIds.indexOfValue because indexOfValue uses == (not .equals)
904 // while also coercing the int primitives to Integer objects.
905 for (int i = 0, nSize = mClientIds.size(); i < nSize; i++) {
906 int mDnsId = mClientIds.valueAt(i);
907 if (globalId == mDnsId) {
908 return mClientIds.keyAt(i);
909 }
910 }
911 return -1;
912 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700913 }
Hugo Benichicbb13672017-04-24 11:35:06 +0900914
915 @VisibleForTesting
916 public interface NsdSettings {
917 boolean isEnabled();
918 void putEnabledStatus(boolean isEnabled);
919
920 static NsdSettings makeDefault(Context context) {
921 ContentResolver resolver = context.getContentResolver();
922 return new NsdSettings() {
923 @Override
924 public boolean isEnabled() {
925 return Settings.Global.getInt(resolver, Settings.Global.NSD_ON, 1) == 1;
926 }
927
928 @Override
929 public void putEnabledStatus(boolean isEnabled) {
930 Settings.Global.putInt(resolver, Settings.Global.NSD_ON, isEnabled ? 1 : 0);
931 }
932 };
933 }
934 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700935}