blob: c5dbc8c32e91fc1ca562a0572417498f07999477 [file] [log] [blame]
Sergey Yakovlev989c44b2016-11-18 18:59:47 +03001/*
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 */
16package com.android.printservice.recommendation.util;
17
18import android.content.Context;
19import android.net.nsd.NsdManager;
20import android.net.nsd.NsdServiceInfo;
21import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.util.Log;
24
25import com.android.internal.annotations.GuardedBy;
26import com.android.internal.util.Preconditions;
27import com.android.printservice.recommendation.PrintServicePlugin;
28
29import java.util.HashSet;
30import java.util.Set;
31
32/**
33 * A discovery listening for mDNS results and only adding the ones that {@link
34 * PrinterFilter#matchesCriteria match} configured list
35 */
36public class MDNSFilteredDiscovery implements NsdManager.DiscoveryListener {
37 private static final String LOG_TAG = "MDNSFilteredDiscovery";
38
39 /**
40 * mDNS service filter interface.
41 * Implement {@link PrinterFilter#matchesCriteria} to filter out supported services
42 */
43 public interface PrinterFilter {
44 /**
45 * Main filter method. Should return true if mDNS service is supported
46 * by the print service plugin
47 *
48 * @param nsdServiceInfo The service info to check
49 *
50 * @return True if service is supported by the print service plugin
51 */
52 boolean matchesCriteria(NsdServiceInfo nsdServiceInfo);
53 }
54
55 /** Printer identifiers of the mPrinters found. */
56 @GuardedBy("mLock")
57 private final @NonNull HashSet<String> mPrinters;
58
59 /** Service types discovered by this plugin */
60 private final @NonNull HashSet<String> mServiceTypes;
61
62 /** Context of the user of this plugin */
63 private final @NonNull Context mContext;
64
65 /** mDNS services filter */
66 private final @NonNull PrinterFilter mPrinterFilter;
67
68 /**
69 * Call back to report the number of mPrinters found.
70 *
71 * We assume that {@link #start} and {@link #stop} are never called in parallel, hence it is
72 * safe to not synchronize access to this field.
73 */
74 private @Nullable PrintServicePlugin.PrinterDiscoveryCallback mCallback;
75
76 /** Queue used to resolve nsd infos */
77 private final @NonNull NsdResolveQueue mResolveQueue;
78
79 /**
80 * Create new stub that assumes that a print service can be used to print on all mPrinters
81 * matching some mDNS names.
82 *
83 * @param context The context the plugin runs in
84 * @param serviceTypes The mDNS service types to listen to.
85 * @param printerFilter The filter for mDNS services
86 */
87 public MDNSFilteredDiscovery(@NonNull Context context,
88 @NonNull Set<String> serviceTypes,
89 @NonNull PrinterFilter printerFilter) {
90 mContext = Preconditions.checkNotNull(context, "context");
91 mServiceTypes = new HashSet<>(Preconditions
92 .checkCollectionNotEmpty(Preconditions.checkCollectionElementsNotNull(serviceTypes,
93 "serviceTypes"), "serviceTypes"));
94 mPrinterFilter = Preconditions.checkNotNull(printerFilter, "printerFilter");
95
96 mResolveQueue = NsdResolveQueue.getInstance();
97 mPrinters = new HashSet<>();
98 }
99
100 /**
101 * @return The NDS manager
102 */
103 private NsdManager getNDSManager() {
104 return (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
105 }
106
107 /**
108 * Start the discovery.
109 *
110 * @param callback Callbacks used by this plugin.
111 */
112 public void start(@NonNull PrintServicePlugin.PrinterDiscoveryCallback callback) {
113 mCallback = callback;
114 mCallback.onChanged(mPrinters.size());
115
116 for (String serviceType : mServiceTypes) {
117 DiscoveryListenerMultiplexer.addListener(getNDSManager(), serviceType, this);
118 }
119 }
120
121 /**
122 * Stop the discovery. This can only return once the plugin is completely finished and cleaned up.
123 */
124 public void stop() {
125 mCallback.onChanged(0);
126 mCallback = null;
127
128 for (int i = 0; i < mServiceTypes.size(); ++i) {
129 DiscoveryListenerMultiplexer.removeListener(getNDSManager(), this);
130 }
131 }
132
133 /**
134 *
135 * @return The number of discovered printers
136 */
137 public int getCount() {
138 return mPrinters.size();
139 }
140
141 @Override
142 public void onStartDiscoveryFailed(String serviceType, int errorCode) {
143 Log.w(LOG_TAG, "Failed to start network discovery for type " + serviceType + ": "
144 + errorCode);
145 }
146
147 @Override
148 public void onStopDiscoveryFailed(String serviceType, int errorCode) {
149 Log.w(LOG_TAG, "Failed to stop network discovery for type " + serviceType + ": "
150 + errorCode);
151 }
152
153 @Override
154 public void onDiscoveryStarted(String serviceType) {
155 // empty
156 }
157
158 @Override
159 public void onDiscoveryStopped(String serviceType) {
160 mPrinters.clear();
161 }
162
163 @Override
164 public void onServiceFound(NsdServiceInfo serviceInfo) {
165 mResolveQueue.resolve(getNDSManager(), serviceInfo,
166 new NsdManager.ResolveListener() {
167 @Override
168 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
169 Log.w(LOG_TAG, "Service found: could not resolve " + serviceInfo + ": " +
170 errorCode);
171 }
172
173 @Override
174 public void onServiceResolved(NsdServiceInfo serviceInfo) {
175 if (mPrinterFilter.matchesCriteria(serviceInfo)) {
176 if (mCallback != null) {
177 boolean added = mPrinters.add(serviceInfo.getHost().getHostAddress());
178 if (added) {
179 mCallback.onChanged(mPrinters.size());
180 }
181 }
182 }
183 }
184 });
185 }
186
187 @Override
188 public void onServiceLost(NsdServiceInfo serviceInfo) {
189 mResolveQueue.resolve(getNDSManager(), serviceInfo,
190 new NsdManager.ResolveListener() {
191 @Override
192 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
193 Log.w(LOG_TAG, "Service lost: Could not resolve " + serviceInfo + ": "
194 + errorCode);
195 }
196
197 @Override
198 public void onServiceResolved(NsdServiceInfo serviceInfo) {
199 if (mPrinterFilter.matchesCriteria(serviceInfo)) {
200 if (mCallback != null) {
201 boolean removed = mPrinters
202 .remove(serviceInfo.getHost().getHostAddress());
203
204 if (removed) {
205 mCallback.onChanged(mPrinters.size());
206 }
207 }
208 }
209 }
210 });
211 }
212}