NsdService.java revision 77b987f1a1bb6028a871de01065b94c4cfff0b5c
1/*
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;
20import android.content.ContentResolver;
21import android.content.Intent;
22import android.content.pm.PackageManager;
23import android.database.ContentObserver;
24import android.net.nsd.NsdServiceInfo;
25import android.net.nsd.DnsSdTxtRecord;
26import android.net.nsd.INsdManager;
27import android.net.nsd.NsdManager;
28import android.os.Binder;
29import android.os.Message;
30import android.os.Messenger;
31import android.os.UserHandle;
32import android.provider.Settings;
33import android.util.Slog;
34import android.util.SparseArray;
35
36import java.io.FileDescriptor;
37import java.io.PrintWriter;
38import java.net.InetAddress;
39import java.util.HashMap;
40import java.util.concurrent.CountDownLatch;
41
42import com.android.internal.util.AsyncChannel;
43import com.android.internal.util.Protocol;
44import com.android.internal.util.State;
45import com.android.internal.util.StateMachine;
46
47/**
48 * Network Service Discovery Service handles remote service discovery operation requests by
49 * implementing the INsdManager interface.
50 *
51 * @hide
52 */
53public class NsdService extends INsdManager.Stub {
54    private static final String TAG = "NsdService";
55    private static final String MDNS_TAG = "mDnsConnector";
56
57    private static final boolean DBG = true;
58
59    private Context mContext;
60    private ContentResolver mContentResolver;
61    private NsdStateMachine mNsdStateMachine;
62
63    /**
64     * Clients receiving asynchronous messages
65     */
66    private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>();
67
68    /* A map from unique id to client info */
69    private SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<ClientInfo>();
70
71    private AsyncChannel mReplyChannel = new AsyncChannel();
72
73    private int INVALID_ID = 0;
74    private int mUniqueId = 1;
75
76    private static final int BASE = Protocol.BASE_NSD_MANAGER;
77    private static final int CMD_TO_STRING_COUNT = NsdManager.RESOLVE_SERVICE - BASE + 1;
78    private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
79
80    static {
81        sCmdToString[NsdManager.DISCOVER_SERVICES - BASE] = "DISCOVER";
82        sCmdToString[NsdManager.STOP_DISCOVERY - BASE] = "STOP-DISCOVER";
83        sCmdToString[NsdManager.REGISTER_SERVICE - BASE] = "REGISTER";
84        sCmdToString[NsdManager.UNREGISTER_SERVICE - BASE] = "UNREGISTER";
85        sCmdToString[NsdManager.RESOLVE_SERVICE - BASE] = "RESOLVE";
86    }
87
88    private static String cmdToString(int cmd) {
89        cmd -= BASE;
90        if ((cmd >= 0) && (cmd < sCmdToString.length)) {
91            return sCmdToString[cmd];
92        } else {
93            return null;
94        }
95    }
96
97    private class NsdStateMachine extends StateMachine {
98
99        private final DefaultState mDefaultState = new DefaultState();
100        private final DisabledState mDisabledState = new DisabledState();
101        private final EnabledState mEnabledState = new EnabledState();
102
103        @Override
104        protected String getWhatToString(int what) {
105            return cmdToString(what);
106        }
107
108        /**
109         * Observes the NSD on/off setting, and takes action when changed.
110         */
111        private void registerForNsdSetting() {
112            ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
113                @Override
114                    public void onChange(boolean selfChange) {
115                        if (isNsdEnabled()) {
116                            mNsdStateMachine.sendMessage(NsdManager.ENABLE);
117                        } else {
118                            mNsdStateMachine.sendMessage(NsdManager.DISABLE);
119                        }
120                    }
121            };
122
123            mContext.getContentResolver().registerContentObserver(
124                    Settings.Global.getUriFor(Settings.Global.NSD_ON),
125                    false, contentObserver);
126        }
127
128        NsdStateMachine(String name) {
129            super(name);
130            addState(mDefaultState);
131                addState(mDisabledState, mDefaultState);
132                addState(mEnabledState, mDefaultState);
133            if (isNsdEnabled()) {
134                setInitialState(mEnabledState);
135            } else {
136                setInitialState(mDisabledState);
137            }
138            setLogRecSize(25);
139            registerForNsdSetting();
140        }
141
142        class DefaultState extends State {
143            @Override
144            public boolean processMessage(Message msg) {
145                switch (msg.what) {
146                    case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
147                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
148                            AsyncChannel c = (AsyncChannel) msg.obj;
149                            if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
150                            c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
151                            ClientInfo cInfo = new ClientInfo(c, msg.replyTo);
152                            mClients.put(msg.replyTo, cInfo);
153                        } else {
154                            Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
155                        }
156                        break;
157                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
158                        if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
159                            Slog.e(TAG, "Send failed, client connection lost");
160                        } else {
161                            if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
162                        }
163                        mClients.remove(msg.replyTo);
164                        break;
165                    case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
166                        AsyncChannel ac = new AsyncChannel();
167                        ac.connect(mContext, getHandler(), msg.replyTo);
168                        break;
169                    case NsdManager.DISCOVER_SERVICES:
170                        replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
171                                NsdManager.FAILURE_INTERNAL_ERROR);
172                       break;
173                    case NsdManager.STOP_DISCOVERY:
174                       replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
175                               NsdManager.FAILURE_INTERNAL_ERROR);
176                        break;
177                    case NsdManager.REGISTER_SERVICE:
178                        replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
179                                NsdManager.FAILURE_INTERNAL_ERROR);
180                        break;
181                    case NsdManager.UNREGISTER_SERVICE:
182                        replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
183                                NsdManager.FAILURE_INTERNAL_ERROR);
184                        break;
185                    case NsdManager.RESOLVE_SERVICE:
186                        replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
187                                NsdManager.FAILURE_INTERNAL_ERROR);
188                        break;
189                    case NsdManager.NATIVE_DAEMON_EVENT:
190                    default:
191                        Slog.e(TAG, "Unhandled " + msg);
192                        return NOT_HANDLED;
193                }
194                return HANDLED;
195            }
196        }
197
198        class DisabledState extends State {
199            @Override
200            public void enter() {
201                sendNsdStateChangeBroadcast(false);
202            }
203
204            @Override
205            public boolean processMessage(Message msg) {
206                switch (msg.what) {
207                    case NsdManager.ENABLE:
208                        transitionTo(mEnabledState);
209                        break;
210                    default:
211                        return NOT_HANDLED;
212                }
213                return HANDLED;
214            }
215        }
216
217        class EnabledState extends State {
218            @Override
219            public void enter() {
220                sendNsdStateChangeBroadcast(true);
221                if (mClients.size() > 0) {
222                    startMDnsDaemon();
223                }
224            }
225
226            @Override
227            public void exit() {
228                if (mClients.size() > 0) {
229                    stopMDnsDaemon();
230                }
231            }
232
233            private boolean requestLimitReached(ClientInfo clientInfo) {
234                if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
235                    if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
236                    return true;
237                }
238                return false;
239            }
240
241            private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
242                clientInfo.mClientIds.put(clientId, globalId);
243                mIdToClientInfoMap.put(globalId, clientInfo);
244            }
245
246            private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
247                clientInfo.mClientIds.remove(clientId);
248                mIdToClientInfoMap.remove(globalId);
249            }
250
251            @Override
252            public boolean processMessage(Message msg) {
253                ClientInfo clientInfo;
254                NsdServiceInfo servInfo;
255                boolean result = HANDLED;
256                int id;
257                switch (msg.what) {
258                  case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
259                        //First client
260                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL &&
261                                mClients.size() == 0) {
262                            startMDnsDaemon();
263                        }
264                        result = NOT_HANDLED;
265                        break;
266                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
267                        //Last client
268                        if (mClients.size() == 1) {
269                            stopMDnsDaemon();
270                        }
271                        result = NOT_HANDLED;
272                        break;
273                    case NsdManager.DISABLE:
274                        //TODO: cleanup clients
275                        transitionTo(mDisabledState);
276                        break;
277                    case NsdManager.DISCOVER_SERVICES:
278                        if (DBG) Slog.d(TAG, "Discover services");
279                        servInfo = (NsdServiceInfo) msg.obj;
280                        clientInfo = mClients.get(msg.replyTo);
281
282                        if (requestLimitReached(clientInfo)) {
283                            replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
284                                    NsdManager.FAILURE_MAX_LIMIT);
285                            break;
286                        }
287
288                        id = getUniqueId();
289                        if (discoverServices(id, servInfo.getServiceType())) {
290                            if (DBG) {
291                                Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
292                                        servInfo.getServiceType());
293                            }
294                            storeRequestMap(msg.arg2, id, clientInfo);
295                            replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
296                        } else {
297                            stopServiceDiscovery(id);
298                            replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
299                                    NsdManager.FAILURE_INTERNAL_ERROR);
300                        }
301                        break;
302                    case NsdManager.STOP_DISCOVERY:
303                        if (DBG) Slog.d(TAG, "Stop service discovery");
304                        clientInfo = mClients.get(msg.replyTo);
305
306                        try {
307                            id = clientInfo.mClientIds.get(msg.arg2).intValue();
308                        } catch (NullPointerException e) {
309                            replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
310                                    NsdManager.FAILURE_INTERNAL_ERROR);
311                            break;
312                        }
313                        removeRequestMap(msg.arg2, id, clientInfo);
314                        if (stopServiceDiscovery(id)) {
315                            replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
316                        } else {
317                            replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
318                                    NsdManager.FAILURE_INTERNAL_ERROR);
319                        }
320                        break;
321                    case NsdManager.REGISTER_SERVICE:
322                        if (DBG) Slog.d(TAG, "Register service");
323                        clientInfo = mClients.get(msg.replyTo);
324                        if (requestLimitReached(clientInfo)) {
325                            replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
326                                    NsdManager.FAILURE_MAX_LIMIT);
327                            break;
328                        }
329
330                        id = getUniqueId();
331                        if (registerService(id, (NsdServiceInfo) msg.obj)) {
332                            if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
333                            storeRequestMap(msg.arg2, id, clientInfo);
334                            // Return success after mDns reports success
335                        } else {
336                            unregisterService(id);
337                            replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
338                                    NsdManager.FAILURE_INTERNAL_ERROR);
339                        }
340                        break;
341                    case NsdManager.UNREGISTER_SERVICE:
342                        if (DBG) Slog.d(TAG, "unregister service");
343                        clientInfo = mClients.get(msg.replyTo);
344                        try {
345                            id = clientInfo.mClientIds.get(msg.arg2).intValue();
346                        } catch (NullPointerException e) {
347                            replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
348                                    NsdManager.FAILURE_INTERNAL_ERROR);
349                            break;
350                        }
351                        removeRequestMap(msg.arg2, id, clientInfo);
352                        if (unregisterService(id)) {
353                            replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
354                        } else {
355                            replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
356                                    NsdManager.FAILURE_INTERNAL_ERROR);
357                        }
358                        break;
359                    case NsdManager.RESOLVE_SERVICE:
360                        if (DBG) Slog.d(TAG, "Resolve service");
361                        servInfo = (NsdServiceInfo) msg.obj;
362                        clientInfo = mClients.get(msg.replyTo);
363
364
365                        if (clientInfo.mResolvedService != null) {
366                            replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
367                                    NsdManager.FAILURE_ALREADY_ACTIVE);
368                            break;
369                        }
370
371                        id = getUniqueId();
372                        if (resolveService(id, servInfo)) {
373                            clientInfo.mResolvedService = new NsdServiceInfo();
374                            storeRequestMap(msg.arg2, id, clientInfo);
375                        } else {
376                            replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
377                                    NsdManager.FAILURE_INTERNAL_ERROR);
378                        }
379                        break;
380                    case NsdManager.NATIVE_DAEMON_EVENT:
381                        NativeEvent event = (NativeEvent) msg.obj;
382                        if (!handleNativeEvent(event.code, event.raw,
383                                NativeDaemonEvent.unescapeArgs(event.raw))) {
384                            result = NOT_HANDLED;
385                        }
386                        break;
387                    default:
388                        result = NOT_HANDLED;
389                        break;
390                }
391                return result;
392            }
393
394            private boolean handleNativeEvent(int code, String raw, String[] cooked) {
395                boolean handled = true;
396                NsdServiceInfo servInfo;
397                int id = Integer.parseInt(cooked[1]);
398                ClientInfo clientInfo = mIdToClientInfoMap.get(id);
399                if (clientInfo == null) {
400                    Slog.e(TAG, "Unique id with no client mapping: " + id);
401                    handled = false;
402                    return handled;
403                }
404
405                /* This goes in response as msg.arg2 */
406                int clientId = -1;
407                int keyId = clientInfo.mClientIds.indexOfValue(id);
408                if (keyId != -1) {
409                    clientId = clientInfo.mClientIds.keyAt(keyId);
410                } else {
411                    // This can happen because of race conditions. For example,
412                    // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
413                    // and we may get in this situation.
414                    Slog.d(TAG, "Notification for a listener that is no longer active: " + id);
415                    handled = false;
416                    return handled;
417                }
418
419                switch (code) {
420                    case NativeResponseCode.SERVICE_FOUND:
421                        /* NNN uniqueId serviceName regType domain */
422                        if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw);
423                        servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
424                        clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
425                                clientId, servInfo);
426                        break;
427                    case NativeResponseCode.SERVICE_LOST:
428                        /* NNN uniqueId serviceName regType domain */
429                        if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw);
430                        servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
431                        clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
432                                clientId, servInfo);
433                        break;
434                    case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
435                        /* NNN uniqueId errorCode */
436                        if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw);
437                        clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
438                                NsdManager.FAILURE_INTERNAL_ERROR, clientId);
439                        break;
440                    case NativeResponseCode.SERVICE_REGISTERED:
441                        /* NNN regId serviceName regType */
442                        if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw);
443                        servInfo = new NsdServiceInfo(cooked[2], null, null);
444                        clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
445                                id, clientId, servInfo);
446                        break;
447                    case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
448                        /* NNN regId errorCode */
449                        if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw);
450                        clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
451                               NsdManager.FAILURE_INTERNAL_ERROR, clientId);
452                        break;
453                    case NativeResponseCode.SERVICE_UPDATED:
454                        /* NNN regId */
455                        break;
456                    case NativeResponseCode.SERVICE_UPDATE_FAILED:
457                        /* NNN regId errorCode */
458                        break;
459                    case NativeResponseCode.SERVICE_RESOLVED:
460                        /* NNN resolveId fullName hostName port txtlen txtdata */
461                        if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
462                        int index = cooked[2].indexOf(".");
463                        if (index == -1) {
464                            Slog.e(TAG, "Invalid service found " + raw);
465                            break;
466                        }
467                        String name = cooked[2].substring(0, index);
468                        String rest = cooked[2].substring(index);
469                        String type = rest.replace(".local.", "");
470
471                        clientInfo.mResolvedService.setServiceName(name);
472                        clientInfo.mResolvedService.setServiceType(type);
473                        clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
474
475                        stopResolveService(id);
476                        removeRequestMap(clientId, id, clientInfo);
477
478                        int id2 = getUniqueId();
479                        if (getAddrInfo(id2, cooked[3])) {
480                            storeRequestMap(clientId, id2, clientInfo);
481                        } else {
482                            clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
483                                    NsdManager.FAILURE_INTERNAL_ERROR, clientId);
484                            clientInfo.mResolvedService = null;
485                        }
486                        break;
487                    case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
488                        /* NNN resolveId errorCode */
489                        if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
490                        stopResolveService(id);
491                        removeRequestMap(clientId, id, clientInfo);
492                        clientInfo.mResolvedService = null;
493                        clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
494                                NsdManager.FAILURE_INTERNAL_ERROR, clientId);
495                        break;
496                    case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
497                        /* NNN resolveId errorCode */
498                        stopGetAddrInfo(id);
499                        removeRequestMap(clientId, id, clientInfo);
500                        clientInfo.mResolvedService = null;
501                        if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
502                        clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
503                                NsdManager.FAILURE_INTERNAL_ERROR, clientId);
504                        break;
505                    case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
506                        /* NNN resolveId hostname ttl addr */
507                        if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw);
508                        try {
509                            clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
510                            clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
511                                   0, clientId, clientInfo.mResolvedService);
512                        } catch (java.net.UnknownHostException e) {
513                            clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
514                                    NsdManager.FAILURE_INTERNAL_ERROR, clientId);
515                        }
516                        stopGetAddrInfo(id);
517                        removeRequestMap(clientId, id, clientInfo);
518                        clientInfo.mResolvedService = null;
519                        break;
520                    default:
521                        handled = false;
522                        break;
523                }
524                return handled;
525            }
526       }
527    }
528
529    private NativeDaemonConnector mNativeConnector;
530    private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1);
531
532    private NsdService(Context context) {
533        mContext = context;
534        mContentResolver = context.getContentResolver();
535
536        mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10,
537                MDNS_TAG, 25, null);
538
539        mNsdStateMachine = new NsdStateMachine(TAG);
540        mNsdStateMachine.start();
541
542        Thread th = new Thread(mNativeConnector, MDNS_TAG);
543        th.start();
544    }
545
546    public static NsdService create(Context context) throws InterruptedException {
547        NsdService service = new NsdService(context);
548        service.mNativeDaemonConnected.await();
549        return service;
550    }
551
552    public Messenger getMessenger() {
553        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET,
554            "NsdService");
555        return new Messenger(mNsdStateMachine.getHandler());
556    }
557
558    public void setEnabled(boolean enable) {
559        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
560                "NsdService");
561        Settings.Global.putInt(mContentResolver, Settings.Global.NSD_ON, enable ? 1 : 0);
562        if (enable) {
563            mNsdStateMachine.sendMessage(NsdManager.ENABLE);
564        } else {
565            mNsdStateMachine.sendMessage(NsdManager.DISABLE);
566        }
567    }
568
569    private void sendNsdStateChangeBroadcast(boolean enabled) {
570        final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
571        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
572        if (enabled) {
573            intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_ENABLED);
574        } else {
575            intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED);
576        }
577        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
578    }
579
580    private boolean isNsdEnabled() {
581        boolean ret = Settings.Global.getInt(mContentResolver, Settings.Global.NSD_ON, 1) == 1;
582        if (DBG) Slog.d(TAG, "Network service discovery enabled " + ret);
583        return ret;
584    }
585
586    private int getUniqueId() {
587        if (++mUniqueId == INVALID_ID) return ++mUniqueId;
588        return mUniqueId;
589    }
590
591    /* These should be in sync with system/netd/mDnsResponseCode.h */
592    class NativeResponseCode {
593        public static final int SERVICE_DISCOVERY_FAILED    =   602;
594        public static final int SERVICE_FOUND               =   603;
595        public static final int SERVICE_LOST                =   604;
596
597        public static final int SERVICE_REGISTRATION_FAILED =   605;
598        public static final int SERVICE_REGISTERED          =   606;
599
600        public static final int SERVICE_RESOLUTION_FAILED   =   607;
601        public static final int SERVICE_RESOLVED            =   608;
602
603        public static final int SERVICE_UPDATED             =   609;
604        public static final int SERVICE_UPDATE_FAILED       =   610;
605
606        public static final int SERVICE_GET_ADDR_FAILED     =   611;
607        public static final int SERVICE_GET_ADDR_SUCCESS    =   612;
608    }
609
610    private class NativeEvent {
611        final int code;
612        final String raw;
613
614        NativeEvent(int code, String raw) {
615            this.code = code;
616            this.raw = raw;
617        }
618    }
619
620    class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
621        public void onDaemonConnected() {
622            mNativeDaemonConnected.countDown();
623        }
624
625        public boolean onCheckHoldWakeLock(int code) {
626            return false;
627        }
628
629        public boolean onEvent(int code, String raw, String[] cooked) {
630            // TODO: NDC translates a message to a callback, we could enhance NDC to
631            // directly interact with a state machine through messages
632            NativeEvent event = new NativeEvent(code, raw);
633            mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
634            return true;
635        }
636    }
637
638    private boolean startMDnsDaemon() {
639        if (DBG) Slog.d(TAG, "startMDnsDaemon");
640        try {
641            mNativeConnector.execute("mdnssd", "start-service");
642        } catch(NativeDaemonConnectorException e) {
643            Slog.e(TAG, "Failed to start daemon" + e);
644            return false;
645        }
646        return true;
647    }
648
649    private boolean stopMDnsDaemon() {
650        if (DBG) Slog.d(TAG, "stopMDnsDaemon");
651        try {
652            mNativeConnector.execute("mdnssd", "stop-service");
653        } catch(NativeDaemonConnectorException e) {
654            Slog.e(TAG, "Failed to start daemon" + e);
655            return false;
656        }
657        return true;
658    }
659
660    private boolean registerService(int regId, NsdServiceInfo service) {
661        if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service);
662        try {
663            //Add txtlen and txtdata
664            mNativeConnector.execute("mdnssd", "register", regId, service.getServiceName(),
665                    service.getServiceType(), service.getPort());
666        } catch(NativeDaemonConnectorException e) {
667            Slog.e(TAG, "Failed to execute registerService " + e);
668            return false;
669        }
670        return true;
671    }
672
673    private boolean unregisterService(int regId) {
674        if (DBG) Slog.d(TAG, "unregisterService: " + regId);
675        try {
676            mNativeConnector.execute("mdnssd", "stop-register", regId);
677        } catch(NativeDaemonConnectorException e) {
678            Slog.e(TAG, "Failed to execute unregisterService " + e);
679            return false;
680        }
681        return true;
682    }
683
684    private boolean updateService(int regId, DnsSdTxtRecord t) {
685        if (DBG) Slog.d(TAG, "updateService: " + regId + " " + t);
686        try {
687            if (t == null) return false;
688            mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData());
689        } catch(NativeDaemonConnectorException e) {
690            Slog.e(TAG, "Failed to updateServices " + e);
691            return false;
692        }
693        return true;
694    }
695
696    private boolean discoverServices(int discoveryId, String serviceType) {
697        if (DBG) Slog.d(TAG, "discoverServices: " + discoveryId + " " + serviceType);
698        try {
699            mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType);
700        } catch(NativeDaemonConnectorException e) {
701            Slog.e(TAG, "Failed to discoverServices " + e);
702            return false;
703        }
704        return true;
705    }
706
707    private boolean stopServiceDiscovery(int discoveryId) {
708        if (DBG) Slog.d(TAG, "stopServiceDiscovery: " + discoveryId);
709        try {
710            mNativeConnector.execute("mdnssd", "stop-discover", discoveryId);
711        } catch(NativeDaemonConnectorException e) {
712            Slog.e(TAG, "Failed to stopServiceDiscovery " + e);
713            return false;
714        }
715        return true;
716    }
717
718    private boolean resolveService(int resolveId, NsdServiceInfo service) {
719        if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service);
720        try {
721            mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(),
722                    service.getServiceType(), "local.");
723        } catch(NativeDaemonConnectorException e) {
724            Slog.e(TAG, "Failed to resolveService " + e);
725            return false;
726        }
727        return true;
728    }
729
730    private boolean stopResolveService(int resolveId) {
731        if (DBG) Slog.d(TAG, "stopResolveService: " + resolveId);
732        try {
733            mNativeConnector.execute("mdnssd", "stop-resolve", resolveId);
734        } catch(NativeDaemonConnectorException e) {
735            Slog.e(TAG, "Failed to stop resolve " + e);
736            return false;
737        }
738        return true;
739    }
740
741    private boolean getAddrInfo(int resolveId, String hostname) {
742        if (DBG) Slog.d(TAG, "getAdddrInfo: " + resolveId);
743        try {
744            mNativeConnector.execute("mdnssd", "getaddrinfo", resolveId, hostname);
745        } catch(NativeDaemonConnectorException e) {
746            Slog.e(TAG, "Failed to getAddrInfo " + e);
747            return false;
748        }
749        return true;
750    }
751
752    private boolean stopGetAddrInfo(int resolveId) {
753        if (DBG) Slog.d(TAG, "stopGetAdddrInfo: " + resolveId);
754        try {
755            mNativeConnector.execute("mdnssd", "stop-getaddrinfo", resolveId);
756        } catch(NativeDaemonConnectorException e) {
757            Slog.e(TAG, "Failed to stopGetAddrInfo " + e);
758            return false;
759        }
760        return true;
761    }
762
763    @Override
764    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
765        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
773        for (ClientInfo client : mClients.values()) {
774            pw.println("Client Info");
775            pw.println(client);
776        }
777
778        mNsdStateMachine.dump(fd, pw, args);
779    }
780
781    /* 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;
787    }
788
789    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);
794    }
795
796    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);
810    }
811
812    /* Information tracked per client */
813    private class ClientInfo {
814
815        private static final int MAX_LIMIT = 10;
816        private final AsyncChannel mChannel;
817        private final Messenger mMessenger;
818        /* Remembers a resolved service until getaddrinfo completes */
819        private NsdServiceInfo mResolvedService;
820
821        /* A map from client id to unique id sent to mDns */
822        private SparseArray<Integer> mClientIds = new SparseArray<Integer>();
823
824        private ClientInfo(AsyncChannel c, Messenger m) {
825            mChannel = c;
826            mMessenger = m;
827            if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
828        }
829
830        @Override
831        public String toString() {
832            StringBuffer sb = new StringBuffer();
833            sb.append("mChannel ").append(mChannel).append("\n");
834            sb.append("mMessenger ").append(mMessenger).append("\n");
835            sb.append("mResolvedService ").append(mResolvedService).append("\n");
836            for(int i = 0; i< mClientIds.size(); i++) {
837                sb.append("clientId ").append(mClientIds.keyAt(i));
838                sb.append(" mDnsId ").append(mClientIds.valueAt(i)).append("\n");
839            }
840            return sb.toString();
841        }
842    }
843}
844