NsdService.java revision 6b2b14028ddec40b70c346f5a1e4d266720fadcd
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                ClientInfo cInfo = null;
146                switch (msg.what) {
147                    case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
148                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
149                            AsyncChannel c = (AsyncChannel) msg.obj;
150                            if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
151                            c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
152                            cInfo = new ClientInfo(c, msg.replyTo);
153                            mClients.put(msg.replyTo, cInfo);
154                        } else {
155                            Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
156                        }
157                        break;
158                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
159                        switch (msg.arg1) {
160                            case AsyncChannel.STATUS_SEND_UNSUCCESSFUL:
161                                Slog.e(TAG, "Send failed, client connection lost");
162                                break;
163                            case AsyncChannel.STATUS_REMOTE_DISCONNECTION:
164                                if (DBG) Slog.d(TAG, "Client disconnected");
165                                break;
166                            default:
167                                if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
168                                break;
169                        }
170                        cInfo = mClients.get(msg.replyTo);
171                        if (cInfo != null) {
172                            cInfo.expungeAllRequests();
173                            mClients.remove(msg.replyTo);
174                        }
175                        //Last client
176                        if (mClients.size() == 0) {
177                            stopMDnsDaemon();
178                        }
179                        break;
180                    case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
181                        AsyncChannel ac = new AsyncChannel();
182                        ac.connect(mContext, getHandler(), msg.replyTo);
183                        break;
184                    case NsdManager.DISCOVER_SERVICES:
185                        replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
186                                NsdManager.FAILURE_INTERNAL_ERROR);
187                       break;
188                    case NsdManager.STOP_DISCOVERY:
189                       replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
190                               NsdManager.FAILURE_INTERNAL_ERROR);
191                        break;
192                    case NsdManager.REGISTER_SERVICE:
193                        replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
194                                NsdManager.FAILURE_INTERNAL_ERROR);
195                        break;
196                    case NsdManager.UNREGISTER_SERVICE:
197                        replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
198                                NsdManager.FAILURE_INTERNAL_ERROR);
199                        break;
200                    case NsdManager.RESOLVE_SERVICE:
201                        replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
202                                NsdManager.FAILURE_INTERNAL_ERROR);
203                        break;
204                    case NsdManager.NATIVE_DAEMON_EVENT:
205                    default:
206                        Slog.e(TAG, "Unhandled " + msg);
207                        return NOT_HANDLED;
208                }
209                return HANDLED;
210            }
211        }
212
213        class DisabledState extends State {
214            @Override
215            public void enter() {
216                sendNsdStateChangeBroadcast(false);
217            }
218
219            @Override
220            public boolean processMessage(Message msg) {
221                switch (msg.what) {
222                    case NsdManager.ENABLE:
223                        transitionTo(mEnabledState);
224                        break;
225                    default:
226                        return NOT_HANDLED;
227                }
228                return HANDLED;
229            }
230        }
231
232        class EnabledState extends State {
233            @Override
234            public void enter() {
235                sendNsdStateChangeBroadcast(true);
236                if (mClients.size() > 0) {
237                    startMDnsDaemon();
238                }
239            }
240
241            @Override
242            public void exit() {
243                if (mClients.size() > 0) {
244                    stopMDnsDaemon();
245                }
246            }
247
248            private boolean requestLimitReached(ClientInfo clientInfo) {
249                if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
250                    if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
251                    return true;
252                }
253                return false;
254            }
255
256            private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
257                clientInfo.mClientIds.put(clientId, globalId);
258                clientInfo.mClientRequests.put(clientId, what);
259                mIdToClientInfoMap.put(globalId, clientInfo);
260            }
261
262            private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
263                clientInfo.mClientIds.remove(clientId);
264                clientInfo.mClientRequests.remove(clientId);
265                mIdToClientInfoMap.remove(globalId);
266            }
267
268            @Override
269            public boolean processMessage(Message msg) {
270                ClientInfo clientInfo;
271                NsdServiceInfo servInfo;
272                boolean result = HANDLED;
273                int id;
274                switch (msg.what) {
275                  case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
276                        //First client
277                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL &&
278                                mClients.size() == 0) {
279                            startMDnsDaemon();
280                        }
281                        result = NOT_HANDLED;
282                        break;
283                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
284                        result = NOT_HANDLED;
285                        break;
286                    case NsdManager.DISABLE:
287                        //TODO: cleanup clients
288                        transitionTo(mDisabledState);
289                        break;
290                    case NsdManager.DISCOVER_SERVICES:
291                        if (DBG) Slog.d(TAG, "Discover services");
292                        servInfo = (NsdServiceInfo) msg.obj;
293                        clientInfo = mClients.get(msg.replyTo);
294
295                        if (requestLimitReached(clientInfo)) {
296                            replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
297                                    NsdManager.FAILURE_MAX_LIMIT);
298                            break;
299                        }
300
301                        id = getUniqueId();
302                        if (discoverServices(id, servInfo.getServiceType())) {
303                            if (DBG) {
304                                Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
305                                        servInfo.getServiceType());
306                            }
307                            storeRequestMap(msg.arg2, id, clientInfo, msg.what);
308                            replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
309                        } else {
310                            stopServiceDiscovery(id);
311                            replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
312                                    NsdManager.FAILURE_INTERNAL_ERROR);
313                        }
314                        break;
315                    case NsdManager.STOP_DISCOVERY:
316                        if (DBG) Slog.d(TAG, "Stop service discovery");
317                        clientInfo = mClients.get(msg.replyTo);
318
319                        try {
320                            id = clientInfo.mClientIds.get(msg.arg2).intValue();
321                        } catch (NullPointerException e) {
322                            replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
323                                    NsdManager.FAILURE_INTERNAL_ERROR);
324                            break;
325                        }
326                        removeRequestMap(msg.arg2, id, clientInfo);
327                        if (stopServiceDiscovery(id)) {
328                            replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
329                        } else {
330                            replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
331                                    NsdManager.FAILURE_INTERNAL_ERROR);
332                        }
333                        break;
334                    case NsdManager.REGISTER_SERVICE:
335                        if (DBG) Slog.d(TAG, "Register service");
336                        clientInfo = mClients.get(msg.replyTo);
337                        if (requestLimitReached(clientInfo)) {
338                            replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
339                                    NsdManager.FAILURE_MAX_LIMIT);
340                            break;
341                        }
342
343                        id = getUniqueId();
344                        if (registerService(id, (NsdServiceInfo) msg.obj)) {
345                            if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
346                            storeRequestMap(msg.arg2, id, clientInfo, msg.what);
347                            // Return success after mDns reports success
348                        } else {
349                            unregisterService(id);
350                            replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
351                                    NsdManager.FAILURE_INTERNAL_ERROR);
352                        }
353                        break;
354                    case NsdManager.UNREGISTER_SERVICE:
355                        if (DBG) Slog.d(TAG, "unregister service");
356                        clientInfo = mClients.get(msg.replyTo);
357                        try {
358                            id = clientInfo.mClientIds.get(msg.arg2).intValue();
359                        } catch (NullPointerException e) {
360                            replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
361                                    NsdManager.FAILURE_INTERNAL_ERROR);
362                            break;
363                        }
364                        removeRequestMap(msg.arg2, id, clientInfo);
365                        if (unregisterService(id)) {
366                            replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
367                        } else {
368                            replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
369                                    NsdManager.FAILURE_INTERNAL_ERROR);
370                        }
371                        break;
372                    case NsdManager.RESOLVE_SERVICE:
373                        if (DBG) Slog.d(TAG, "Resolve service");
374                        servInfo = (NsdServiceInfo) msg.obj;
375                        clientInfo = mClients.get(msg.replyTo);
376
377
378                        if (clientInfo.mResolvedService != null) {
379                            replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
380                                    NsdManager.FAILURE_ALREADY_ACTIVE);
381                            break;
382                        }
383
384                        id = getUniqueId();
385                        if (resolveService(id, servInfo)) {
386                            clientInfo.mResolvedService = new NsdServiceInfo();
387                            storeRequestMap(msg.arg2, id, clientInfo, msg.what);
388                        } else {
389                            replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
390                                    NsdManager.FAILURE_INTERNAL_ERROR);
391                        }
392                        break;
393                    case NsdManager.NATIVE_DAEMON_EVENT:
394                        NativeEvent event = (NativeEvent) msg.obj;
395                        if (!handleNativeEvent(event.code, event.raw,
396                                NativeDaemonEvent.unescapeArgs(event.raw))) {
397                            result = NOT_HANDLED;
398                        }
399                        break;
400                    default:
401                        result = NOT_HANDLED;
402                        break;
403                }
404                return result;
405            }
406
407            private boolean handleNativeEvent(int code, String raw, String[] cooked) {
408                boolean handled = true;
409                NsdServiceInfo servInfo;
410                int id = Integer.parseInt(cooked[1]);
411                ClientInfo clientInfo = mIdToClientInfoMap.get(id);
412                if (clientInfo == null) {
413                    Slog.e(TAG, "Unique id with no client mapping: " + id);
414                    handled = false;
415                    return handled;
416                }
417
418                /* This goes in response as msg.arg2 */
419                int clientId = -1;
420                int keyId = clientInfo.mClientIds.indexOfValue(id);
421                if (keyId != -1) {
422                    clientId = clientInfo.mClientIds.keyAt(keyId);
423                } else {
424                    // This can happen because of race conditions. For example,
425                    // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
426                    // and we may get in this situation.
427                    Slog.d(TAG, "Notification for a listener that is no longer active: " + id);
428                    handled = false;
429                    return handled;
430                }
431
432                switch (code) {
433                    case NativeResponseCode.SERVICE_FOUND:
434                        /* NNN uniqueId serviceName regType domain */
435                        if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw);
436                        servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
437                        clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
438                                clientId, servInfo);
439                        break;
440                    case NativeResponseCode.SERVICE_LOST:
441                        /* NNN uniqueId serviceName regType domain */
442                        if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw);
443                        servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
444                        clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
445                                clientId, servInfo);
446                        break;
447                    case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
448                        /* NNN uniqueId errorCode */
449                        if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw);
450                        clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
451                                NsdManager.FAILURE_INTERNAL_ERROR, clientId);
452                        break;
453                    case NativeResponseCode.SERVICE_REGISTERED:
454                        /* NNN regId serviceName regType */
455                        if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw);
456                        servInfo = new NsdServiceInfo(cooked[2], null, null);
457                        clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
458                                id, clientId, servInfo);
459                        break;
460                    case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
461                        /* NNN regId errorCode */
462                        if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw);
463                        clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
464                               NsdManager.FAILURE_INTERNAL_ERROR, clientId);
465                        break;
466                    case NativeResponseCode.SERVICE_UPDATED:
467                        /* NNN regId */
468                        break;
469                    case NativeResponseCode.SERVICE_UPDATE_FAILED:
470                        /* NNN regId errorCode */
471                        break;
472                    case NativeResponseCode.SERVICE_RESOLVED:
473                        /* NNN resolveId fullName hostName port txtlen txtdata */
474                        if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
475                        int index = cooked[2].indexOf(".");
476                        if (index == -1) {
477                            Slog.e(TAG, "Invalid service found " + raw);
478                            break;
479                        }
480                        String name = cooked[2].substring(0, index);
481                        String rest = cooked[2].substring(index);
482                        String type = rest.replace(".local.", "");
483
484                        clientInfo.mResolvedService.setServiceName(name);
485                        clientInfo.mResolvedService.setServiceType(type);
486                        clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
487
488                        stopResolveService(id);
489                        removeRequestMap(clientId, id, clientInfo);
490
491                        int id2 = getUniqueId();
492                        if (getAddrInfo(id2, cooked[3])) {
493                            storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
494                        } else {
495                            clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
496                                    NsdManager.FAILURE_INTERNAL_ERROR, clientId);
497                            clientInfo.mResolvedService = null;
498                        }
499                        break;
500                    case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
501                        /* NNN resolveId errorCode */
502                        if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
503                        stopResolveService(id);
504                        removeRequestMap(clientId, id, clientInfo);
505                        clientInfo.mResolvedService = null;
506                        clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
507                                NsdManager.FAILURE_INTERNAL_ERROR, clientId);
508                        break;
509                    case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
510                        /* NNN resolveId errorCode */
511                        stopGetAddrInfo(id);
512                        removeRequestMap(clientId, id, clientInfo);
513                        clientInfo.mResolvedService = null;
514                        if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
515                        clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
516                                NsdManager.FAILURE_INTERNAL_ERROR, clientId);
517                        break;
518                    case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
519                        /* NNN resolveId hostname ttl addr */
520                        if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw);
521                        try {
522                            clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
523                            clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
524                                   0, clientId, clientInfo.mResolvedService);
525                        } catch (java.net.UnknownHostException e) {
526                            clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
527                                    NsdManager.FAILURE_INTERNAL_ERROR, clientId);
528                        }
529                        stopGetAddrInfo(id);
530                        removeRequestMap(clientId, id, clientInfo);
531                        clientInfo.mResolvedService = null;
532                        break;
533                    default:
534                        handled = false;
535                        break;
536                }
537                return handled;
538            }
539       }
540    }
541
542    private NativeDaemonConnector mNativeConnector;
543    private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1);
544
545    private NsdService(Context context) {
546        mContext = context;
547        mContentResolver = context.getContentResolver();
548
549        mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10,
550                MDNS_TAG, 25, null);
551
552        mNsdStateMachine = new NsdStateMachine(TAG);
553        mNsdStateMachine.start();
554
555        Thread th = new Thread(mNativeConnector, MDNS_TAG);
556        th.start();
557    }
558
559    public static NsdService create(Context context) throws InterruptedException {
560        NsdService service = new NsdService(context);
561        service.mNativeDaemonConnected.await();
562        return service;
563    }
564
565    public Messenger getMessenger() {
566        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET,
567            "NsdService");
568        return new Messenger(mNsdStateMachine.getHandler());
569    }
570
571    public void setEnabled(boolean enable) {
572        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
573                "NsdService");
574        Settings.Global.putInt(mContentResolver, Settings.Global.NSD_ON, enable ? 1 : 0);
575        if (enable) {
576            mNsdStateMachine.sendMessage(NsdManager.ENABLE);
577        } else {
578            mNsdStateMachine.sendMessage(NsdManager.DISABLE);
579        }
580    }
581
582    private void sendNsdStateChangeBroadcast(boolean enabled) {
583        final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
584        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
585        if (enabled) {
586            intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_ENABLED);
587        } else {
588            intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED);
589        }
590        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
591    }
592
593    private boolean isNsdEnabled() {
594        boolean ret = Settings.Global.getInt(mContentResolver, Settings.Global.NSD_ON, 1) == 1;
595        if (DBG) Slog.d(TAG, "Network service discovery enabled " + ret);
596        return ret;
597    }
598
599    private int getUniqueId() {
600        if (++mUniqueId == INVALID_ID) return ++mUniqueId;
601        return mUniqueId;
602    }
603
604    /* These should be in sync with system/netd/mDnsResponseCode.h */
605    class NativeResponseCode {
606        public static final int SERVICE_DISCOVERY_FAILED    =   602;
607        public static final int SERVICE_FOUND               =   603;
608        public static final int SERVICE_LOST                =   604;
609
610        public static final int SERVICE_REGISTRATION_FAILED =   605;
611        public static final int SERVICE_REGISTERED          =   606;
612
613        public static final int SERVICE_RESOLUTION_FAILED   =   607;
614        public static final int SERVICE_RESOLVED            =   608;
615
616        public static final int SERVICE_UPDATED             =   609;
617        public static final int SERVICE_UPDATE_FAILED       =   610;
618
619        public static final int SERVICE_GET_ADDR_FAILED     =   611;
620        public static final int SERVICE_GET_ADDR_SUCCESS    =   612;
621    }
622
623    private class NativeEvent {
624        final int code;
625        final String raw;
626
627        NativeEvent(int code, String raw) {
628            this.code = code;
629            this.raw = raw;
630        }
631    }
632
633    class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
634        public void onDaemonConnected() {
635            mNativeDaemonConnected.countDown();
636        }
637
638        public boolean onCheckHoldWakeLock(int code) {
639            return false;
640        }
641
642        public boolean onEvent(int code, String raw, String[] cooked) {
643            // TODO: NDC translates a message to a callback, we could enhance NDC to
644            // directly interact with a state machine through messages
645            NativeEvent event = new NativeEvent(code, raw);
646            mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
647            return true;
648        }
649    }
650
651    private boolean startMDnsDaemon() {
652        if (DBG) Slog.d(TAG, "startMDnsDaemon");
653        try {
654            mNativeConnector.execute("mdnssd", "start-service");
655        } catch(NativeDaemonConnectorException e) {
656            Slog.e(TAG, "Failed to start daemon" + e);
657            return false;
658        }
659        return true;
660    }
661
662    private boolean stopMDnsDaemon() {
663        if (DBG) Slog.d(TAG, "stopMDnsDaemon");
664        try {
665            mNativeConnector.execute("mdnssd", "stop-service");
666        } catch(NativeDaemonConnectorException e) {
667            Slog.e(TAG, "Failed to start daemon" + e);
668            return false;
669        }
670        return true;
671    }
672
673    private boolean registerService(int regId, NsdServiceInfo service) {
674        if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service);
675        try {
676            //Add txtlen and txtdata
677            mNativeConnector.execute("mdnssd", "register", regId, service.getServiceName(),
678                    service.getServiceType(), service.getPort());
679        } catch(NativeDaemonConnectorException e) {
680            Slog.e(TAG, "Failed to execute registerService " + e);
681            return false;
682        }
683        return true;
684    }
685
686    private boolean unregisterService(int regId) {
687        if (DBG) Slog.d(TAG, "unregisterService: " + regId);
688        try {
689            mNativeConnector.execute("mdnssd", "stop-register", regId);
690        } catch(NativeDaemonConnectorException e) {
691            Slog.e(TAG, "Failed to execute unregisterService " + e);
692            return false;
693        }
694        return true;
695    }
696
697    private boolean updateService(int regId, DnsSdTxtRecord t) {
698        if (DBG) Slog.d(TAG, "updateService: " + regId + " " + t);
699        try {
700            if (t == null) return false;
701            mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData());
702        } catch(NativeDaemonConnectorException e) {
703            Slog.e(TAG, "Failed to updateServices " + e);
704            return false;
705        }
706        return true;
707    }
708
709    private boolean discoverServices(int discoveryId, String serviceType) {
710        if (DBG) Slog.d(TAG, "discoverServices: " + discoveryId + " " + serviceType);
711        try {
712            mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType);
713        } catch(NativeDaemonConnectorException e) {
714            Slog.e(TAG, "Failed to discoverServices " + e);
715            return false;
716        }
717        return true;
718    }
719
720    private boolean stopServiceDiscovery(int discoveryId) {
721        if (DBG) Slog.d(TAG, "stopServiceDiscovery: " + discoveryId);
722        try {
723            mNativeConnector.execute("mdnssd", "stop-discover", discoveryId);
724        } catch(NativeDaemonConnectorException e) {
725            Slog.e(TAG, "Failed to stopServiceDiscovery " + e);
726            return false;
727        }
728        return true;
729    }
730
731    private boolean resolveService(int resolveId, NsdServiceInfo service) {
732        if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service);
733        try {
734            mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(),
735                    service.getServiceType(), "local.");
736        } catch(NativeDaemonConnectorException e) {
737            Slog.e(TAG, "Failed to resolveService " + e);
738            return false;
739        }
740        return true;
741    }
742
743    private boolean stopResolveService(int resolveId) {
744        if (DBG) Slog.d(TAG, "stopResolveService: " + resolveId);
745        try {
746            mNativeConnector.execute("mdnssd", "stop-resolve", resolveId);
747        } catch(NativeDaemonConnectorException e) {
748            Slog.e(TAG, "Failed to stop resolve " + e);
749            return false;
750        }
751        return true;
752    }
753
754    private boolean getAddrInfo(int resolveId, String hostname) {
755        if (DBG) Slog.d(TAG, "getAdddrInfo: " + resolveId);
756        try {
757            mNativeConnector.execute("mdnssd", "getaddrinfo", resolveId, hostname);
758        } catch(NativeDaemonConnectorException e) {
759            Slog.e(TAG, "Failed to getAddrInfo " + e);
760            return false;
761        }
762        return true;
763    }
764
765    private boolean stopGetAddrInfo(int resolveId) {
766        if (DBG) Slog.d(TAG, "stopGetAdddrInfo: " + resolveId);
767        try {
768            mNativeConnector.execute("mdnssd", "stop-getaddrinfo", resolveId);
769        } catch(NativeDaemonConnectorException e) {
770            Slog.e(TAG, "Failed to stopGetAddrInfo " + e);
771            return false;
772        }
773        return true;
774    }
775
776    @Override
777    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
778        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
779                != PackageManager.PERMISSION_GRANTED) {
780            pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid="
781                    + Binder.getCallingPid()
782                    + ", uid=" + Binder.getCallingUid());
783            return;
784        }
785
786        for (ClientInfo client : mClients.values()) {
787            pw.println("Client Info");
788            pw.println(client);
789        }
790
791        mNsdStateMachine.dump(fd, pw, args);
792    }
793
794    /* arg2 on the source message has an id that needs to be retained in replies
795     * see NsdManager for details */
796    private Message obtainMessage(Message srcMsg) {
797        Message msg = Message.obtain();
798        msg.arg2 = srcMsg.arg2;
799        return msg;
800    }
801
802    private void replyToMessage(Message msg, int what) {
803        if (msg.replyTo == null) return;
804        Message dstMsg = obtainMessage(msg);
805        dstMsg.what = what;
806        mReplyChannel.replyToMessage(msg, dstMsg);
807    }
808
809    private void replyToMessage(Message msg, int what, int arg1) {
810        if (msg.replyTo == null) return;
811        Message dstMsg = obtainMessage(msg);
812        dstMsg.what = what;
813        dstMsg.arg1 = arg1;
814        mReplyChannel.replyToMessage(msg, dstMsg);
815    }
816
817    private void replyToMessage(Message msg, int what, Object obj) {
818        if (msg.replyTo == null) return;
819        Message dstMsg = obtainMessage(msg);
820        dstMsg.what = what;
821        dstMsg.obj = obj;
822        mReplyChannel.replyToMessage(msg, dstMsg);
823    }
824
825    /* Information tracked per client */
826    private class ClientInfo {
827
828        private static final int MAX_LIMIT = 10;
829        private final AsyncChannel mChannel;
830        private final Messenger mMessenger;
831        /* Remembers a resolved service until getaddrinfo completes */
832        private NsdServiceInfo mResolvedService;
833
834        /* A map from client id to unique id sent to mDns */
835        private SparseArray<Integer> mClientIds = new SparseArray<Integer>();
836
837        /* A map from client id to the type of the request we had received */
838        private SparseArray<Integer> mClientRequests = new SparseArray<Integer>();
839
840        private ClientInfo(AsyncChannel c, Messenger m) {
841            mChannel = c;
842            mMessenger = m;
843            if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
844        }
845
846        @Override
847        public String toString() {
848            StringBuffer sb = new StringBuffer();
849            sb.append("mChannel ").append(mChannel).append("\n");
850            sb.append("mMessenger ").append(mMessenger).append("\n");
851            sb.append("mResolvedService ").append(mResolvedService).append("\n");
852            for(int i = 0; i< mClientIds.size(); i++) {
853                int clientID = mClientIds.keyAt(i);
854                sb.append("clientId ").append(clientID).
855                    append(" mDnsId ").append(mClientIds.valueAt(i)).
856                    append(" type ").append(mClientRequests.get(clientID)).append("\n");
857            }
858            return sb.toString();
859        }
860
861        // Remove any pending requests from the global map when we get rid of a client,
862        // and send cancellations to the daemon.
863        private void expungeAllRequests() {
864            int globalId, clientId, i;
865            for (i = 0; i < mClientIds.size(); i++) {
866                clientId = mClientIds.keyAt(i);
867                globalId = mClientIds.valueAt(i);
868                mIdToClientInfoMap.remove(globalId);
869                if (DBG) Slog.d(TAG, "Terminating client-ID " + clientId +
870                        " global-ID " + globalId + " type " + mClientRequests.get(clientId));
871                switch (mClientRequests.get(clientId)) {
872                    case NsdManager.DISCOVER_SERVICES:
873                        stopServiceDiscovery(globalId);
874                        break;
875                    case NsdManager.RESOLVE_SERVICE:
876                        stopResolveService(globalId);
877                        break;
878                    case NsdManager.REGISTER_SERVICE:
879                        unregisterService(globalId);
880                        break;
881                    default:
882                        break;
883                }
884            }
885            mClientIds.clear();
886            mClientRequests.clear();
887        }
888
889    }
890}
891