blob: 2158f60089d7852353f50273e1a7a2636843e1db [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) {
Hugo Benichi2d968b42017-04-24 16:41:03 +0900102 notifyEnabled(isNsdEnabled());
Hugo Benichi2183ba92017-04-05 14:06:11 +0900103 }
Irfan Sheriff919aca52012-06-01 16:44:13 -0700104 };
105
106 mContext.getContentResolver().registerContentObserver(
Jeff Sharkey625239a2012-09-26 22:03:49 -0700107 Settings.Global.getUriFor(Settings.Global.NSD_ON),
Irfan Sheriff919aca52012-06-01 16:44:13 -0700108 false, contentObserver);
109 }
110
Hugo Benichicbb13672017-04-24 11:35:06 +0900111 NsdStateMachine(String name, Handler handler) {
112 super(name, handler);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700113 addState(mDefaultState);
114 addState(mDisabledState, mDefaultState);
115 addState(mEnabledState, mDefaultState);
Hugo Benichi2d968b42017-04-24 16:41:03 +0900116 State initialState = isNsdEnabled() ? mEnabledState : mDisabledState;
117 setInitialState(initialState);
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700118 setLogRecSize(25);
Irfan Sheriff919aca52012-06-01 16:44:13 -0700119 registerForNsdSetting();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700120 }
121
122 class DefaultState extends State {
123 @Override
124 public boolean processMessage(Message msg) {
Dave Plattf31760c2014-03-07 14:48:22 -0800125 ClientInfo cInfo = null;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700126 switch (msg.what) {
127 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
128 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
129 AsyncChannel c = (AsyncChannel) msg.obj;
130 if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
131 c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
Dave Plattf31760c2014-03-07 14:48:22 -0800132 cInfo = new ClientInfo(c, msg.replyTo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700133 mClients.put(msg.replyTo, cInfo);
134 } else {
135 Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
136 }
137 break;
138 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Dave Plattf31760c2014-03-07 14:48:22 -0800139 switch (msg.arg1) {
140 case AsyncChannel.STATUS_SEND_UNSUCCESSFUL:
141 Slog.e(TAG, "Send failed, client connection lost");
142 break;
143 case AsyncChannel.STATUS_REMOTE_DISCONNECTION:
144 if (DBG) Slog.d(TAG, "Client disconnected");
145 break;
146 default:
147 if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
148 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700149 }
Dave Plattf31760c2014-03-07 14:48:22 -0800150 cInfo = mClients.get(msg.replyTo);
151 if (cInfo != null) {
152 cInfo.expungeAllRequests();
153 mClients.remove(msg.replyTo);
154 }
155 //Last client
156 if (mClients.size() == 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900157 mDaemon.stop();
Dave Plattf31760c2014-03-07 14:48:22 -0800158 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700159 break;
160 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
161 AsyncChannel ac = new AsyncChannel();
162 ac.connect(mContext, getHandler(), msg.replyTo);
163 break;
164 case NsdManager.DISCOVER_SERVICES:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700165 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
166 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700167 break;
168 case NsdManager.STOP_DISCOVERY:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700169 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
170 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700171 break;
172 case NsdManager.REGISTER_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700173 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
174 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700175 break;
176 case NsdManager.UNREGISTER_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700177 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
178 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700179 break;
180 case NsdManager.RESOLVE_SERVICE:
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700181 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
182 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700183 break;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700184 case NsdManager.NATIVE_DAEMON_EVENT:
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700185 default:
186 Slog.e(TAG, "Unhandled " + msg);
187 return NOT_HANDLED;
188 }
189 return HANDLED;
190 }
191 }
192
193 class DisabledState extends State {
194 @Override
195 public void enter() {
196 sendNsdStateChangeBroadcast(false);
197 }
198
199 @Override
200 public boolean processMessage(Message msg) {
201 switch (msg.what) {
202 case NsdManager.ENABLE:
203 transitionTo(mEnabledState);
204 break;
205 default:
206 return NOT_HANDLED;
207 }
208 return HANDLED;
209 }
210 }
211
212 class EnabledState extends State {
213 @Override
214 public void enter() {
215 sendNsdStateChangeBroadcast(true);
216 if (mClients.size() > 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900217 mDaemon.start();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700218 }
219 }
220
221 @Override
222 public void exit() {
223 if (mClients.size() > 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900224 mDaemon.stop();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700225 }
226 }
227
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700228 private boolean requestLimitReached(ClientInfo clientInfo) {
229 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
230 if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
231 return true;
232 }
233 return false;
234 }
235
Dave Plattf31760c2014-03-07 14:48:22 -0800236 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700237 clientInfo.mClientIds.put(clientId, globalId);
Dave Plattf31760c2014-03-07 14:48:22 -0800238 clientInfo.mClientRequests.put(clientId, what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700239 mIdToClientInfoMap.put(globalId, clientInfo);
240 }
241
242 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900243 clientInfo.mClientIds.delete(clientId);
244 clientInfo.mClientRequests.delete(clientId);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700245 mIdToClientInfoMap.remove(globalId);
246 }
247
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700248 @Override
249 public boolean processMessage(Message msg) {
250 ClientInfo clientInfo;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700251 NsdServiceInfo servInfo;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700252 int id;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700253 switch (msg.what) {
Hugo Benichi23dba852017-04-05 14:43:29 +0900254 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700255 //First client
256 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL &&
257 mClients.size() == 0) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900258 mDaemon.start();
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700259 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900260 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700261 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Hugo Benichi23dba852017-04-05 14:43:29 +0900262 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700263 case NsdManager.DISABLE:
264 //TODO: cleanup clients
265 transitionTo(mDisabledState);
266 break;
267 case NsdManager.DISCOVER_SERVICES:
268 if (DBG) Slog.d(TAG, "Discover services");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700269 servInfo = (NsdServiceInfo) msg.obj;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700270 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700271
272 if (requestLimitReached(clientInfo)) {
273 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
274 NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700275 break;
276 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700277
278 id = getUniqueId();
279 if (discoverServices(id, servInfo.getServiceType())) {
280 if (DBG) {
281 Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
282 servInfo.getServiceType());
283 }
Dave Plattf31760c2014-03-07 14:48:22 -0800284 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700285 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700286 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700287 stopServiceDiscovery(id);
288 replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
289 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700290 }
291 break;
292 case NsdManager.STOP_DISCOVERY:
293 if (DBG) Slog.d(TAG, "Stop service discovery");
294 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700295
296 try {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900297 id = clientInfo.mClientIds.get(msg.arg2);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700298 } catch (NullPointerException e) {
299 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
300 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700301 break;
302 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700303 removeRequestMap(msg.arg2, id, clientInfo);
304 if (stopServiceDiscovery(id)) {
305 replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700306 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700307 replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
308 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700309 }
310 break;
311 case NsdManager.REGISTER_SERVICE:
312 if (DBG) Slog.d(TAG, "Register service");
313 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700314 if (requestLimitReached(clientInfo)) {
315 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
316 NsdManager.FAILURE_MAX_LIMIT);
317 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700318 }
319
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700320 id = getUniqueId();
321 if (registerService(id, (NsdServiceInfo) msg.obj)) {
322 if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
Dave Plattf31760c2014-03-07 14:48:22 -0800323 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700324 // Return success after mDns reports success
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700325 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700326 unregisterService(id);
327 replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
328 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700329 }
330 break;
331 case NsdManager.UNREGISTER_SERVICE:
332 if (DBG) Slog.d(TAG, "unregister service");
333 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700334 try {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900335 id = clientInfo.mClientIds.get(msg.arg2);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700336 } catch (NullPointerException e) {
337 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
338 NsdManager.FAILURE_INTERNAL_ERROR);
339 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700340 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700341 removeRequestMap(msg.arg2, id, clientInfo);
342 if (unregisterService(id)) {
343 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
344 } else {
345 replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
346 NsdManager.FAILURE_INTERNAL_ERROR);
347 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700348 break;
349 case NsdManager.RESOLVE_SERVICE:
350 if (DBG) Slog.d(TAG, "Resolve service");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700351 servInfo = (NsdServiceInfo) msg.obj;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700352 clientInfo = mClients.get(msg.replyTo);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700353
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700354
355 if (clientInfo.mResolvedService != null) {
356 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
357 NsdManager.FAILURE_ALREADY_ACTIVE);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700358 break;
359 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700360
361 id = getUniqueId();
362 if (resolveService(id, servInfo)) {
363 clientInfo.mResolvedService = new NsdServiceInfo();
Dave Plattf31760c2014-03-07 14:48:22 -0800364 storeRequestMap(msg.arg2, id, clientInfo, msg.what);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700365 } else {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700366 replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
367 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700368 }
369 break;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700370 case NsdManager.NATIVE_DAEMON_EVENT:
371 NativeEvent event = (NativeEvent) msg.obj;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700372 if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
Hugo Benichi23dba852017-04-05 14:43:29 +0900373 return NOT_HANDLED;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700374 }
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700375 break;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700376 default:
Hugo Benichi23dba852017-04-05 14:43:29 +0900377 return NOT_HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700378 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900379 return HANDLED;
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700380 }
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700381
382 private boolean handleNativeEvent(int code, String raw, String[] cooked) {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700383 NsdServiceInfo servInfo;
384 int id = Integer.parseInt(cooked[1]);
385 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
386 if (clientInfo == null) {
Hugo Benichi2183ba92017-04-05 14:06:11 +0900387 String name = NativeResponseCode.nameOf(code);
388 Slog.e(TAG, String.format("id %d for %s has no client mapping", id, name));
Hugo Benichi23dba852017-04-05 14:43:29 +0900389 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700390 }
391
392 /* This goes in response as msg.arg2 */
Christopher Lane5a577902014-04-25 18:39:07 -0700393 int clientId = clientInfo.getClientId(id);
394 if (clientId < 0) {
Vinit Deshapnde8ed09e82013-06-25 19:45:03 -0700395 // This can happen because of race conditions. For example,
396 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
397 // and we may get in this situation.
Hugo Benichi2183ba92017-04-05 14:06:11 +0900398 String name = NativeResponseCode.nameOf(code);
399 Slog.d(TAG, String.format(
400 "Notification %s for listener id %d that is no longer active",
401 name, id));
Hugo Benichi23dba852017-04-05 14:43:29 +0900402 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700403 }
Hugo Benichi2183ba92017-04-05 14:06:11 +0900404 if (DBG) {
405 String name = NativeResponseCode.nameOf(code);
406 Slog.d(TAG, String.format("Native daemon message %s: %s", name, raw));
407 }
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700408 switch (code) {
409 case NativeResponseCode.SERVICE_FOUND:
410 /* NNN uniqueId serviceName regType domain */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700411 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700412 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
413 clientId, servInfo);
414 break;
415 case NativeResponseCode.SERVICE_LOST:
416 /* NNN uniqueId serviceName regType domain */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700417 servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700418 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
419 clientId, servInfo);
420 break;
421 case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
422 /* NNN uniqueId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700423 clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
424 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
425 break;
426 case NativeResponseCode.SERVICE_REGISTERED:
427 /* NNN regId serviceName regType */
Christopher Laneb72d8b42014-03-17 16:35:45 -0700428 servInfo = new NsdServiceInfo(cooked[2], null);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700429 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
430 id, clientId, servInfo);
431 break;
432 case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
433 /* NNN regId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700434 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
435 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
436 break;
437 case NativeResponseCode.SERVICE_UPDATED:
438 /* NNN regId */
439 break;
440 case NativeResponseCode.SERVICE_UPDATE_FAILED:
441 /* NNN regId errorCode */
442 break;
443 case NativeResponseCode.SERVICE_RESOLVED:
444 /* NNN resolveId fullName hostName port txtlen txtdata */
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700445 int index = 0;
446 while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
447 if (cooked[2].charAt(index) == '\\') {
448 ++index;
449 }
450 ++index;
451 }
452 if (index >= cooked[2].length()) {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700453 Slog.e(TAG, "Invalid service found " + raw);
454 break;
455 }
456 String name = cooked[2].substring(0, index);
457 String rest = cooked[2].substring(index);
458 String type = rest.replace(".local.", "");
459
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700460 name = unescape(name);
461
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700462 clientInfo.mResolvedService.setServiceName(name);
463 clientInfo.mResolvedService.setServiceType(type);
464 clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
Philip P. Moltmann312c61e2016-03-16 10:15:39 -0700465 clientInfo.mResolvedService.setTxtRecords(cooked[6]);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700466
467 stopResolveService(id);
Vinit Deshapnde40387242013-11-12 15:36:37 -0800468 removeRequestMap(clientId, id, clientInfo);
469
470 int id2 = getUniqueId();
471 if (getAddrInfo(id2, cooked[3])) {
Dave Plattf31760c2014-03-07 14:48:22 -0800472 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
Vinit Deshapnde40387242013-11-12 15:36:37 -0800473 } else {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700474 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
475 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700476 clientInfo.mResolvedService = null;
477 }
478 break;
479 case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
480 /* NNN resolveId errorCode */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700481 stopResolveService(id);
482 removeRequestMap(clientId, id, clientInfo);
483 clientInfo.mResolvedService = null;
484 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
485 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
486 break;
487 case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
488 /* NNN resolveId errorCode */
489 stopGetAddrInfo(id);
490 removeRequestMap(clientId, id, clientInfo);
491 clientInfo.mResolvedService = null;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700492 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
493 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
494 break;
495 case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
496 /* NNN resolveId hostname ttl addr */
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700497 try {
498 clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
499 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
500 0, clientId, clientInfo.mResolvedService);
501 } catch (java.net.UnknownHostException e) {
502 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
503 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
504 }
505 stopGetAddrInfo(id);
506 removeRequestMap(clientId, id, clientInfo);
507 clientInfo.mResolvedService = null;
508 break;
509 default:
Hugo Benichi23dba852017-04-05 14:43:29 +0900510 return false;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700511 }
Hugo Benichi23dba852017-04-05 14:43:29 +0900512 return true;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700513 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700514 }
515 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700516
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700517 private String unescape(String s) {
518 StringBuilder sb = new StringBuilder(s.length());
519 for (int i = 0; i < s.length(); ++i) {
520 char c = s.charAt(i);
521 if (c == '\\') {
522 if (++i >= s.length()) {
523 Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
524 break;
525 }
526 c = s.charAt(i);
527 if (c != '.' && c != '\\') {
528 if (i + 2 >= s.length()) {
529 Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
530 break;
531 }
532 c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
533 i += 2;
534 }
535 }
536 sb.append(c);
537 }
538 return sb.toString();
539 }
540
Hugo Benichicbb13672017-04-24 11:35:06 +0900541 @VisibleForTesting
Hugo Benichi1fac3192017-04-24 16:19:58 +0900542 NsdService(Context ctx, NsdSettings settings, Handler handler, DaemonConnectionSupplier fn) {
Hugo Benichicbb13672017-04-24 11:35:06 +0900543 mContext = ctx;
544 mNsdSettings = settings;
Hugo Benichicbb13672017-04-24 11:35:06 +0900545 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700546 mNsdStateMachine.start();
Hugo Benichi1fac3192017-04-24 16:19:58 +0900547 mDaemonCallback = new NativeCallbackReceiver();
548 mDaemon = fn.get(mDaemonCallback);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700549 }
550
551 public static NsdService create(Context context) throws InterruptedException {
Hugo Benichicbb13672017-04-24 11:35:06 +0900552 NsdSettings settings = NsdSettings.makeDefault(context);
553 HandlerThread thread = new HandlerThread(TAG);
554 thread.start();
555 Handler handler = new Handler(thread.getLooper());
Hugo Benichi1fac3192017-04-24 16:19:58 +0900556 NsdService service = new NsdService(context, settings, handler, DaemonConnection::new);
557 service.mDaemonCallback.awaitConnection();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700558 return service;
559 }
560
561 public Messenger getMessenger() {
Hugo Benichicbb13672017-04-24 11:35:06 +0900562 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700563 return new Messenger(mNsdStateMachine.getHandler());
564 }
565
Hugo Benichi2d968b42017-04-24 16:41:03 +0900566 public void setEnabled(boolean isEnabled) {
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700567 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
568 "NsdService");
Hugo Benichi2d968b42017-04-24 16:41:03 +0900569 mNsdSettings.putEnabledStatus(isEnabled);
570 notifyEnabled(isEnabled);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700571 }
572
Hugo Benichi2d968b42017-04-24 16:41:03 +0900573 private void notifyEnabled(boolean isEnabled) {
574 mNsdStateMachine.sendMessage(isEnabled ? NsdManager.ENABLE : NsdManager.DISABLE);
575 }
576
577 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff54ac7a52012-04-19 10:26:34 -0700578 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700579 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi2d968b42017-04-24 16:41:03 +0900580 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
581 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700582 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700583 }
584
585 private boolean isNsdEnabled() {
Hugo Benichicbb13672017-04-24 11:35:06 +0900586 boolean ret = mNsdSettings.isEnabled();
587 if (DBG) {
588 Slog.d(TAG, "Network service discovery is " + (ret ? "enabled" : "disabled"));
589 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700590 return ret;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700591 }
592
Irfan Sheriff817388e2012-04-11 14:52:19 -0700593 private int getUniqueId() {
594 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
595 return mUniqueId;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700596 }
597
Sreeram Ramachandran03666c72014-07-19 23:21:46 -0700598 /* These should be in sync with system/netd/server/ResponseCode.h */
Hugo Benichi2183ba92017-04-05 14:06:11 +0900599 static final class NativeResponseCode {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700600 public static final int SERVICE_DISCOVERY_FAILED = 602;
601 public static final int SERVICE_FOUND = 603;
602 public static final int SERVICE_LOST = 604;
603
604 public static final int SERVICE_REGISTRATION_FAILED = 605;
605 public static final int SERVICE_REGISTERED = 606;
606
607 public static final int SERVICE_RESOLUTION_FAILED = 607;
608 public static final int SERVICE_RESOLVED = 608;
609
610 public static final int SERVICE_UPDATED = 609;
611 public static final int SERVICE_UPDATE_FAILED = 610;
612
613 public static final int SERVICE_GET_ADDR_FAILED = 611;
614 public static final int SERVICE_GET_ADDR_SUCCESS = 612;
Hugo Benichi2183ba92017-04-05 14:06:11 +0900615
616 private static final SparseArray<String> CODE_NAMES = new SparseArray<>();
617 static {
618 CODE_NAMES.put(SERVICE_DISCOVERY_FAILED, "SERVICE_DISCOVERY_FAILED");
619 CODE_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND");
620 CODE_NAMES.put(SERVICE_LOST, "SERVICE_LOST");
621 CODE_NAMES.put(SERVICE_REGISTRATION_FAILED, "SERVICE_REGISTRATION_FAILED");
622 CODE_NAMES.put(SERVICE_REGISTERED, "SERVICE_REGISTERED");
623 CODE_NAMES.put(SERVICE_RESOLUTION_FAILED, "SERVICE_RESOLUTION_FAILED");
624 CODE_NAMES.put(SERVICE_RESOLVED, "SERVICE_RESOLVED");
625 CODE_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
626 CODE_NAMES.put(SERVICE_UPDATE_FAILED, "SERVICE_UPDATE_FAILED");
627 CODE_NAMES.put(SERVICE_GET_ADDR_FAILED, "SERVICE_GET_ADDR_FAILED");
628 CODE_NAMES.put(SERVICE_GET_ADDR_SUCCESS, "SERVICE_GET_ADDR_SUCCESS");
629 }
630
631 static String nameOf(int code) {
632 String name = CODE_NAMES.get(code);
633 if (name == null) {
634 return Integer.toString(code);
635 }
636 return name;
637 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700638 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700639
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700640 private class NativeEvent {
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700641 final int code;
642 final String raw;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700643 final String[] cooked;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700644
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700645 NativeEvent(int code, String raw, String[] cooked) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700646 this.code = code;
647 this.raw = raw;
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700648 this.cooked = cooked;
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700649 }
650 }
651
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700652 class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900653 private final CountDownLatch connected = new CountDownLatch(1);
654
655 public void awaitConnection() throws InterruptedException {
656 connected.await();
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700657 }
658
Hugo Benichi1fac3192017-04-24 16:19:58 +0900659 @Override
660 public void onDaemonConnected() {
661 connected.countDown();
662 }
663
664 @Override
Dianne Hackborn77b987f2014-02-26 16:20:52 -0800665 public boolean onCheckHoldWakeLock(int code) {
666 return false;
667 }
668
Hugo Benichi1fac3192017-04-24 16:19:58 +0900669 @Override
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700670 public boolean onEvent(int code, String raw, String[] cooked) {
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700671 // TODO: NDC translates a message to a callback, we could enhance NDC to
672 // directly interact with a state machine through messages
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700673 NativeEvent event = new NativeEvent(code, raw, cooked);
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700674 mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
675 return true;
676 }
677 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700678
Hugo Benichi1fac3192017-04-24 16:19:58 +0900679 interface DaemonConnectionSupplier {
680 DaemonConnection get(NativeCallbackReceiver callback);
681 }
682
683 @VisibleForTesting
684 public static class DaemonConnection {
685 final NativeDaemonConnector mNativeConnector;
686
687 DaemonConnection(NativeCallbackReceiver callback) {
688 mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
689 new Thread(mNativeConnector, MDNS_TAG).start();
Irfan Sheriff817388e2012-04-11 14:52:19 -0700690 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900691
692 public boolean execute(Object... args) {
693 if (DBG) {
694 Slog.d(TAG, "mdnssd " + Arrays.toString(args));
695 }
696 try {
697 mNativeConnector.execute("mdnssd", args);
698 } catch (NativeDaemonConnectorException e) {
699 Slog.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
700 return false;
701 }
702 return true;
703 }
704
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900705 public void start() {
706 execute("start-service");
Hugo Benichi1fac3192017-04-24 16:19:58 +0900707 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900708
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900709 public void stop() {
710 execute("stop-service");
711 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700712 }
713
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700714 private boolean registerService(int regId, NsdServiceInfo service) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900715 if (DBG) {
716 Slog.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700717 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900718 String name = service.getServiceName();
719 String type = service.getServiceType();
720 int port = service.getPort();
721 byte[] textRecord = service.getTxtRecord();
722 String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900723 return mDaemon.execute("register", regId, name, type, port, record);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700724 }
725
Irfan Sheriff817388e2012-04-11 14:52:19 -0700726 private boolean unregisterService(int regId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900727 return mDaemon.execute("stop-register", regId);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700728 }
729
730 private boolean updateService(int regId, DnsSdTxtRecord t) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900731 if (t == null) {
Irfan Sheriff817388e2012-04-11 14:52:19 -0700732 return false;
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700733 }
Hugo Benichi1fac3192017-04-24 16:19:58 +0900734 return mDaemon.execute("update", regId, t.size(), t.getRawData());
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700735 }
736
Irfan Sheriff817388e2012-04-11 14:52:19 -0700737 private boolean discoverServices(int discoveryId, String serviceType) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900738 return mDaemon.execute("discover", discoveryId, serviceType);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700739 }
740
Irfan Sheriff817388e2012-04-11 14:52:19 -0700741 private boolean stopServiceDiscovery(int discoveryId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900742 return mDaemon.execute("stop-discover", discoveryId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700743 }
744
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700745 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900746 String name = service.getServiceName();
747 String type = service.getServiceType();
748 return mDaemon.execute("resolve", resolveId, name, type, "local.");
Irfan Sheriff817388e2012-04-11 14:52:19 -0700749 }
750
751 private boolean stopResolveService(int resolveId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900752 return mDaemon.execute("stop-resolve", resolveId);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700753 }
754
755 private boolean getAddrInfo(int resolveId, String hostname) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900756 return mDaemon.execute("getaddrinfo", resolveId, hostname);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700757 }
758
759 private boolean stopGetAddrInfo(int resolveId) {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900760 return mDaemon.execute("stop-getaddrinfo", resolveId);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700761 }
762
763 @Override
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700764 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700765 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
766 != PackageManager.PERMISSION_GRANTED) {
767 pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid="
768 + Binder.getCallingPid()
769 + ", uid=" + Binder.getCallingUid());
770 return;
771 }
772
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700773 for (ClientInfo client : mClients.values()) {
774 pw.println("Client Info");
775 pw.println(client);
776 }
777
778 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700779 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700780
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700781 /* arg2 on the source message has an id that needs to be retained in replies
782 * see NsdManager for details */
783 private Message obtainMessage(Message srcMsg) {
784 Message msg = Message.obtain();
785 msg.arg2 = srcMsg.arg2;
786 return msg;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700787 }
788
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700789 private void replyToMessage(Message msg, int what) {
790 if (msg.replyTo == null) return;
791 Message dstMsg = obtainMessage(msg);
792 dstMsg.what = what;
793 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700794 }
795
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700796 private void replyToMessage(Message msg, int what, int arg1) {
797 if (msg.replyTo == null) return;
798 Message dstMsg = obtainMessage(msg);
799 dstMsg.what = what;
800 dstMsg.arg1 = arg1;
801 mReplyChannel.replyToMessage(msg, dstMsg);
802 }
803
804 private void replyToMessage(Message msg, int what, Object obj) {
805 if (msg.replyTo == null) return;
806 Message dstMsg = obtainMessage(msg);
807 dstMsg.what = what;
808 dstMsg.obj = obj;
809 mReplyChannel.replyToMessage(msg, dstMsg);
Irfan Sheriff817388e2012-04-11 14:52:19 -0700810 }
811
812 /* Information tracked per client */
813 private class ClientInfo {
814
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700815 private static final int MAX_LIMIT = 10;
Vairavan Srinivasan6727e732012-08-05 13:14:12 -0700816 private final AsyncChannel mChannel;
817 private final Messenger mMessenger;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700818 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700819 private NsdServiceInfo mResolvedService;
820
821 /* A map from client id to unique id sent to mDns */
Hugo Benichi0f86b442017-04-11 14:42:47 +0900822 private final SparseIntArray mClientIds = new SparseIntArray();
Irfan Sheriff817388e2012-04-11 14:52:19 -0700823
Dave Plattf31760c2014-03-07 14:48:22 -0800824 /* A map from client id to the type of the request we had received */
Hugo Benichi0f86b442017-04-11 14:42:47 +0900825 private final SparseIntArray mClientRequests = new SparseIntArray();
Dave Plattf31760c2014-03-07 14:48:22 -0800826
Irfan Sheriff817388e2012-04-11 14:52:19 -0700827 private ClientInfo(AsyncChannel c, Messenger m) {
828 mChannel = c;
829 mMessenger = m;
Irfan Sheriff817388e2012-04-11 14:52:19 -0700830 if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
831 }
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700832
833 @Override
834 public String toString() {
835 StringBuffer sb = new StringBuffer();
836 sb.append("mChannel ").append(mChannel).append("\n");
837 sb.append("mMessenger ").append(mMessenger).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700838 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Irfan Sheriff22af38c2012-05-03 16:44:27 -0700839 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattf31760c2014-03-07 14:48:22 -0800840 int clientID = mClientIds.keyAt(i);
841 sb.append("clientId ").append(clientID).
842 append(" mDnsId ").append(mClientIds.valueAt(i)).
843 append(" type ").append(mClientRequests.get(clientID)).append("\n");
Irfan Sheriff3ef889b2012-04-17 23:15:29 -0700844 }
845 return sb.toString();
846 }
Dave Plattf31760c2014-03-07 14:48:22 -0800847
848 // Remove any pending requests from the global map when we get rid of a client,
849 // and send cancellations to the daemon.
850 private void expungeAllRequests() {
851 int globalId, clientId, i;
Hugo Benichi0f86b442017-04-11 14:42:47 +0900852 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Dave Plattf31760c2014-03-07 14:48:22 -0800853 for (i = 0; i < mClientIds.size(); i++) {
854 clientId = mClientIds.keyAt(i);
855 globalId = mClientIds.valueAt(i);
856 mIdToClientInfoMap.remove(globalId);
857 if (DBG) Slog.d(TAG, "Terminating client-ID " + clientId +
858 " global-ID " + globalId + " type " + mClientRequests.get(clientId));
859 switch (mClientRequests.get(clientId)) {
860 case NsdManager.DISCOVER_SERVICES:
861 stopServiceDiscovery(globalId);
862 break;
863 case NsdManager.RESOLVE_SERVICE:
864 stopResolveService(globalId);
865 break;
866 case NsdManager.REGISTER_SERVICE:
867 unregisterService(globalId);
868 break;
869 default:
870 break;
871 }
872 }
873 mClientIds.clear();
874 mClientRequests.clear();
875 }
876
Christopher Lane5a577902014-04-25 18:39:07 -0700877 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
878 // return the corresponding listener id. mDnsClient id is also called a global id.
879 private int getClientId(final int globalId) {
Hugo Benichi0f86b442017-04-11 14:42:47 +0900880 int idx = mClientIds.indexOfValue(globalId);
881 if (idx < 0) {
882 return idx;
Christopher Lane5a577902014-04-25 18:39:07 -0700883 }
Hugo Benichi0f86b442017-04-11 14:42:47 +0900884 return mClientIds.keyAt(idx);
Christopher Lane5a577902014-04-25 18:39:07 -0700885 }
Irfan Sheriff817388e2012-04-11 14:52:19 -0700886 }
Hugo Benichicbb13672017-04-24 11:35:06 +0900887
888 @VisibleForTesting
889 public interface NsdSettings {
890 boolean isEnabled();
891 void putEnabledStatus(boolean isEnabled);
892
893 static NsdSettings makeDefault(Context context) {
894 ContentResolver resolver = context.getContentResolver();
895 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 }
905 };
906 }
907 }
Irfan Sheriff7d024d32012-03-22 17:01:39 -0700908}