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