DiscoveryListenerMultiplexer.java revision bfaa47233215996b8554a4b7a1a7b36bb3eaf607
1/*
2 * Copyright (C) 2016 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.printservice.recommendation.util;
18
19import android.annotation.NonNull;
20import android.net.nsd.NsdManager;
21import android.net.nsd.NsdServiceInfo;
22import android.util.ArrayMap;
23import android.util.Log;
24
25import java.util.ArrayList;
26
27/**
28 * Used to multiplex listening for NSD services. This is needed as only a limited amount of
29 * {@link NsdManager.DiscoveryListener listeners} are allowed.
30 */
31public class DiscoveryListenerMultiplexer {
32    private static final String LOG_TAG = "DiscoveryListenerMx";
33
34    /** List of registered {@link DiscoveryListenerSet discovery sets}. */
35    private static final @NonNull ArrayMap<String, DiscoveryListenerSet> sListeners =
36            new ArrayMap<>();
37
38    /**
39     * Add a new {@link NsdManager.DiscoveryListener listener} for a {@code serviceType}.
40     *
41     * @param nsdManager  The {@link NsdManager NSD manager} to use
42     * @param serviceType The service type to listen for
43     * @param newListener the {@link NsdManager.DiscoveryListener listener} to add.
44     */
45    public static void addListener(@NonNull NsdManager nsdManager, @NonNull String serviceType,
46            @NonNull NsdManager.DiscoveryListener newListener) {
47        synchronized (sListeners) {
48            DiscoveryListenerSet listenerSet = sListeners.get(serviceType);
49
50            if (listenerSet == null) {
51                ArrayList<NsdManager.DiscoveryListener> subListeners = new ArrayList<>(1);
52                listenerSet = new DiscoveryListenerSet(subListeners,
53                        new MultiListener(subListeners));
54
55                sListeners.put(serviceType, listenerSet);
56            }
57
58            synchronized (listenerSet.subListeners) {
59                if (listenerSet.subListeners.isEmpty()) {
60                    nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
61                            listenerSet.mainListener);
62                }
63
64                listenerSet.subListeners.add(newListener);
65            }
66        }
67    }
68
69    /**
70     * Remove a previously added {@link NsdManager.DiscoveryListener listener}.
71     *
72     * @param nsdManager The {@link NsdManager NSD manager} to use
73     * @param listener   The {@link NsdManager.DiscoveryListener listener} that was registered
74     *
75     * @return true iff the listener was removed
76     */
77    public static boolean removeListener(@NonNull NsdManager nsdManager,
78            @NonNull NsdManager.DiscoveryListener listener) {
79        boolean wasRemoved = false;
80
81        synchronized (sListeners) {
82            for (DiscoveryListenerSet listeners : sListeners.values()) {
83                synchronized (listeners) {
84                    wasRemoved = listeners.subListeners.remove(listener);
85
86                    if (wasRemoved) {
87                        if (listeners.subListeners.isEmpty()) {
88                            nsdManager.stopServiceDiscovery(listeners.mainListener);
89                        }
90
91                        break;
92                    }
93                }
94            }
95        }
96
97        return wasRemoved;
98    }
99
100    /** Private class holding all data for a service type */
101    private static class DiscoveryListenerSet {
102        /** The plugin's listeners */
103        final @NonNull ArrayList<NsdManager.DiscoveryListener> subListeners;
104
105        /** The listener registered with the NSD Manager */
106        final @NonNull MultiListener mainListener;
107
108        private DiscoveryListenerSet(ArrayList<NsdManager.DiscoveryListener> subListeners,
109                MultiListener mainListener) {
110            this.subListeners = subListeners;
111            this.mainListener = mainListener;
112        }
113    }
114
115    /**
116     * A {@link NsdManager.DiscoveryListener} that calls a list of registered listeners when
117     * a service is found or lost.
118     */
119    private static class MultiListener implements NsdManager.DiscoveryListener {
120        private final @NonNull ArrayList<NsdManager.DiscoveryListener> mListeners;
121
122        /**
123         * Create a new multi listener.
124         *
125         * @param listeners The listeners to forward the calls.
126         */
127        public MultiListener(@NonNull ArrayList<NsdManager.DiscoveryListener> listeners) {
128            mListeners = listeners;
129        }
130
131        @Override
132        public void onStartDiscoveryFailed(String serviceType, int errorCode) {
133            Log.w(LOG_TAG, "Failed to start network discovery for type " + serviceType + ": "
134                    + errorCode);
135        }
136
137        @Override
138        public void onStopDiscoveryFailed(String serviceType, int errorCode) {
139            Log.w(LOG_TAG, "Failed to stop network discovery for type " + serviceType + ": "
140                    + errorCode);
141        }
142
143        @Override
144        public void onDiscoveryStarted(String serviceType) {
145            // not implemented
146        }
147
148        @Override
149        public void onDiscoveryStopped(String serviceType) {
150            // not implemented
151        }
152
153        @Override
154        public void onServiceFound(NsdServiceInfo serviceInfo) {
155            synchronized (mListeners) {
156                int numListeners = mListeners.size();
157                for (int i = 0; i < numListeners; i++) {
158                    NsdManager.DiscoveryListener listener = mListeners.get(i);
159
160                    listener.onServiceFound(serviceInfo);
161                }
162            }
163        }
164
165        @Override
166        public void onServiceLost(NsdServiceInfo serviceInfo) {
167            synchronized (mListeners) {
168                int numListeners = mListeners.size();
169                for (int i = 0; i < numListeners; i++) {
170                    NsdManager.DiscoveryListener listener = mListeners.get(i);
171
172                    listener.onServiceLost(serviceInfo);
173                }
174            }
175        }
176    }
177}
178