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