blob: a2c0485f3d0ad09c1ed0900c72d5221cc8295acf [file] [log] [blame]
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -07001/*
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.plugin.mdnsFilter;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.annotation.StringRes;
22import android.content.Context;
23import android.net.nsd.NsdManager;
24import android.net.nsd.NsdServiceInfo;
25import android.util.Log;
26import com.android.internal.annotations.GuardedBy;
27import com.android.internal.util.Preconditions;
28import com.android.printservice.recommendation.PrintServicePlugin;
Philip P. Moltmannbfaa4722016-05-03 13:35:28 -070029import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer;
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -070030import com.android.printservice.recommendation.util.NsdResolveQueue;
31
32import java.util.HashSet;
33import java.util.List;
34
35/**
36 * A plugin listening for mDNS results and only adding the ones that {@link
37 * MDNSUtils#isVendorPrinter match} configured list
38 */
39public class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.DiscoveryListener {
40 private static final String LOG_TAG = "MDNSFilterPlugin";
41
42 private static final String PRINTER_SERVICE_TYPE = "_ipp._tcp";
43
44 /** Name of the print service this plugin is for */
45 private final @StringRes int mName;
46
47 /** Package name of the print service this plugin is for */
48 private final @NonNull CharSequence mPackageName;
49
50 /** mDNS names handled by the print service this plugin is for */
51 private final @NonNull HashSet<String> mMDNSNames;
52
53 /** Printer identifiers of the mPrinters found. */
54 @GuardedBy("mLock")
55 private final @NonNull HashSet<String> mPrinters;
56
57 /** Context of the user of this plugin */
58 private final @NonNull Context mContext;
59
60 /**
61 * Call back to report the number of mPrinters found.
62 *
63 * We assume that {@link #start} and {@link #stop} are never called in parallel, hence it is
64 * safe to not synchronize access to this field.
65 */
66 private @Nullable PrinterDiscoveryCallback mCallback;
67
68 /** Queue used to resolve nsd infos */
69 private final @NonNull NsdResolveQueue mResolveQueue;
70
71 /**
72 * Create new stub that assumes that a print service can be used to print on all mPrinters
73 * matching some mDNS names.
74 *
75 * @param context The context the plugin runs in
76 * @param name The user friendly name of the print service
77 * @param packageName The package name of the print service
78 * @param mDNSNames The mDNS names of the printer.
79 */
80 public MDNSFilterPlugin(@NonNull Context context, @NonNull String name,
81 @NonNull CharSequence packageName, @NonNull List<String> mDNSNames) {
82 mContext = Preconditions.checkNotNull(context, "context");
83 mName = mContext.getResources().getIdentifier(Preconditions.checkStringNotEmpty(name,
Philip P. Moltmannbfaa4722016-05-03 13:35:28 -070084 "name"), null, "com.android.printservice.recommendation");
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -070085 mPackageName = Preconditions.checkStringNotEmpty(packageName);
86 mMDNSNames = new HashSet<>(Preconditions
87 .checkCollectionNotEmpty(Preconditions.checkCollectionElementsNotNull(mDNSNames,
88 "mDNSNames"), "mDNSNames"));
89
90 mResolveQueue = NsdResolveQueue.getInstance();
91 mPrinters = new HashSet<>();
92 }
93
94 @Override
95 public @NonNull CharSequence getPackageName() {
96 return mPackageName;
97 }
98
99 /**
100 * @return The NDS manager
101 */
102 private NsdManager getNDSManager() {
103 return (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
104 }
105
106 @Override
107 public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
108 mCallback = callback;
109
Philip P. Moltmannbfaa4722016-05-03 13:35:28 -0700110 DiscoveryListenerMultiplexer.addListener(getNDSManager(), PRINTER_SERVICE_TYPE, this);
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700111 }
112
113 @Override
114 public @StringRes int getName() {
115 return mName;
116 }
117
118 @Override
119 public void stop() throws Exception {
120 mCallback.onChanged(0);
121 mCallback = null;
122
Philip P. Moltmannbfaa4722016-05-03 13:35:28 -0700123 DiscoveryListenerMultiplexer.removeListener(getNDSManager(), this);
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700124 }
125
126 @Override
127 public void onStartDiscoveryFailed(String serviceType, int errorCode) {
128 Log.w(LOG_TAG, "Failed to start network discovery for type " + serviceType + ": "
129 + errorCode);
130 }
131
132 @Override
133 public void onStopDiscoveryFailed(String serviceType, int errorCode) {
134 Log.w(LOG_TAG, "Failed to stop network discovery for type " + serviceType + ": "
135 + errorCode);
136 }
137
138 @Override
139 public void onDiscoveryStarted(String serviceType) {
140 // empty
141 }
142
143 @Override
144 public void onDiscoveryStopped(String serviceType) {
145 mPrinters.clear();
146 }
147
148 @Override
149 public void onServiceFound(NsdServiceInfo serviceInfo) {
150 mResolveQueue.resolve(getNDSManager(), serviceInfo,
151 new NsdManager.ResolveListener() {
152 @Override
153 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
154 Log.w(LOG_TAG, "Service found: could not resolve " + serviceInfo + ": " +
155 errorCode);
156 }
157
158 @Override
159 public void onServiceResolved(NsdServiceInfo serviceInfo) {
160 if (MDNSUtils.isVendorPrinter(serviceInfo, mMDNSNames)) {
161 if (mCallback != null) {
162 boolean added = mPrinters.add(serviceInfo.getHost().getHostAddress());
163
164 if (added) {
165 mCallback.onChanged(mPrinters.size());
166 }
167 }
168 }
169 }
170 });
171 }
172
173 @Override
174 public void onServiceLost(NsdServiceInfo serviceInfo) {
175 mResolveQueue.resolve(getNDSManager(), serviceInfo,
176 new NsdManager.ResolveListener() {
177 @Override
178 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
179 Log.w(LOG_TAG, "Service lost: Could not resolve " + serviceInfo + ": "
180 + errorCode);
181 }
182
183 @Override
184 public void onServiceResolved(NsdServiceInfo serviceInfo) {
185 if (MDNSUtils.isVendorPrinter(serviceInfo, mMDNSNames)) {
186 if (mCallback != null) {
187 boolean removed = mPrinters
188 .remove(serviceInfo.getHost().getHostAddress());
189
190 if (removed) {
191 mCallback.onChanged(mPrinters.size());
192 }
193 }
194 }
195 }
196 });
197 }
198}