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.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; 29import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer; 30import 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, 84 "name"), null, "com.android.printservice.recommendation"); 85 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 110 DiscoveryListenerMultiplexer.addListener(getNDSManager(), PRINTER_SERVICE_TYPE, this); 111 } 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 123 DiscoveryListenerMultiplexer.removeListener(getNDSManager(), this); 124 } 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} 199