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.hp; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.net.nsd.NsdManager; 22import android.net.nsd.NsdServiceInfo; 23import android.text.TextUtils; 24import android.util.Pair; 25 26import java.util.ArrayList; 27import java.util.HashMap; 28import java.util.List; 29import java.util.Map; 30import java.util.Set; 31 32import com.android.printservice.recommendation.R; 33import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer; 34 35public class ServiceListener implements ServiceResolveQueue.ResolveCallback { 36 37 private final NsdManager mNSDManager; 38 private final Map<String, VendorInfo> mVendorInfoHashMap; 39 private final String[] mServiceType; 40 private final Observer mObserver; 41 private final ServiceResolveQueue mResolveQueue; 42 private List<NsdManager.DiscoveryListener> mListeners = new ArrayList<>(); 43 public HashMap<String, PrinterHashMap> mVendorHashMap = new HashMap<>(); 44 45 public interface Observer { 46 boolean matchesCriteria(String vendor, NsdServiceInfo serviceInfo); 47 void dataSetChanged(); 48 } 49 50 public ServiceListener(Context context, Observer observer, String[] serviceTypes) { 51 mObserver = observer; 52 mServiceType = serviceTypes; 53 mNSDManager = (NsdManager)context.getSystemService(Context.NSD_SERVICE); 54 mResolveQueue = ServiceResolveQueue.getInstance(mNSDManager); 55 56 Map<String, VendorInfo> vendorInfoMap = new HashMap<>(); 57 TypedArray testArray = context.getResources().obtainTypedArray(R.array.known_print_plugin_vendors); 58 for(int i = 0; i < testArray.length(); i++) { 59 int arrayID = testArray.getResourceId(i, 0); 60 if (arrayID != 0) { 61 VendorInfo info = new VendorInfo(context.getResources(), arrayID); 62 vendorInfoMap.put(info.mVendorID, info); 63 vendorInfoMap.put(info.mPackageName, info); 64 } 65 } 66 testArray.recycle(); 67 mVendorInfoHashMap = vendorInfoMap; 68 } 69 70 @Override 71 public void serviceResolved(NsdServiceInfo nsdServiceInfo) { 72 printerFound(nsdServiceInfo); 73 } 74 75 private synchronized void printerFound(NsdServiceInfo nsdServiceInfo) { 76 if (nsdServiceInfo == null) return; 77 if (TextUtils.isEmpty(PrinterHashMap.getKey(nsdServiceInfo))) return; 78 String vendor = MDnsUtils.getVendor(nsdServiceInfo); 79 if (vendor == null) vendor = ""; 80 for(Map.Entry<String,VendorInfo> entry : mVendorInfoHashMap.entrySet()) { 81 for(String vendorValues : entry.getValue().mDNSValues) { 82 if (vendor.equalsIgnoreCase(vendorValues)) { 83 vendor = entry.getValue().mVendorID; 84 break; 85 } 86 } 87 // intentional pointer check 88 //noinspection StringEquality 89 if ((vendor != entry.getValue().mVendorID) && 90 MDnsUtils.isVendorPrinter(nsdServiceInfo, entry.getValue().mDNSValues)) { 91 vendor = entry.getValue().mVendorID; 92 } 93 // intentional pointer check 94 //noinspection StringEquality 95 if (vendor == entry.getValue().mVendorID) break; 96 } 97 98 if (TextUtils.isEmpty(vendor)) { 99 return; 100 } 101 102 if (!mObserver.matchesCriteria(vendor, nsdServiceInfo)) 103 return; 104 boolean mapsChanged; 105 106 PrinterHashMap vendorHash = mVendorHashMap.get(vendor); 107 if (vendorHash == null) { 108 vendorHash = new PrinterHashMap(); 109 } 110 mapsChanged = (vendorHash.addPrinter(nsdServiceInfo) == null); 111 mVendorHashMap.put(vendor, vendorHash); 112 113 if (mapsChanged) { 114 mObserver.dataSetChanged(); 115 } 116 } 117 118 private synchronized void printerRemoved(NsdServiceInfo nsdServiceInfo) { 119 boolean wasRemoved = false; 120 Set<String> vendors = mVendorHashMap.keySet(); 121 for(String vendor : vendors) { 122 PrinterHashMap map = mVendorHashMap.get(vendor); 123 wasRemoved |= (map.removePrinter(nsdServiceInfo) != null); 124 if (map.isEmpty()) wasRemoved |= (mVendorHashMap.remove(vendor) != null); 125 } 126 if (wasRemoved) { 127 mObserver.dataSetChanged(); 128 } 129 } 130 131 public void start() { 132 stop(); 133 for(final String service :mServiceType) { 134 NsdManager.DiscoveryListener listener = new NsdManager.DiscoveryListener() { 135 @Override 136 public void onStartDiscoveryFailed(String s, int i) { 137 138 } 139 140 @Override 141 public void onStopDiscoveryFailed(String s, int i) { 142 143 } 144 145 @Override 146 public void onDiscoveryStarted(String s) { 147 148 } 149 150 @Override 151 public void onDiscoveryStopped(String s) { 152 153 } 154 155 @Override 156 public void onServiceFound(NsdServiceInfo nsdServiceInfo) { 157 mResolveQueue.queueRequest(nsdServiceInfo, ServiceListener.this); 158 } 159 160 @Override 161 public void onServiceLost(NsdServiceInfo nsdServiceInfo) { 162 mResolveQueue.removeRequest(nsdServiceInfo, ServiceListener.this); 163 printerRemoved(nsdServiceInfo); 164 } 165 }; 166 DiscoveryListenerMultiplexer.addListener(mNSDManager, service, listener); 167 mListeners.add(listener); 168 } 169 } 170 171 public void stop() { 172 for(NsdManager.DiscoveryListener listener : mListeners) { 173 DiscoveryListenerMultiplexer.removeListener(mNSDManager, listener); 174 } 175 mVendorHashMap.clear(); 176 mListeners.clear(); 177 } 178 179 public Pair<Integer, Integer> getCount() { 180 int count = 0; 181 for (PrinterHashMap map : mVendorHashMap.values()) { 182 count += map.size(); 183 } 184 return Pair.create(mVendorHashMap.size(), count); 185 } 186} 187