Merge "Merge "Add HP and Mopria print recommendation service" into nyc-dev am: 9e5d4e078a am: e87fdd9c16" into nyc-mr1-dev-plus-aosp

This commit is contained in:
Android Build Merger (Role)
2016-05-04 21:46:29 +00:00
committed by Android (Google) Code Review
15 changed files with 905 additions and 17 deletions

View File

@@ -15,4 +15,25 @@
-->
<resources>
<string name="app_label">Print Service Recommendation Service</string>
<!-- HP / Mopria plugin -->
<string-array name="known_print_vendor_info_for_mopria" translatable="false">
<item>org.mopria.printplugin</item>
<item>WFDS</item>
<!-- no specific mDNS values to list -->
</string-array>
<string-array name="known_print_vendor_info_for_hp" translatable="false">
<item>com.hp.android.printservice</item>
<item>HP</item>
<!-- HP has used these values in mDNS records over the years -->
<item>HP</item>
<item>Hewlett-Packard</item>
<item>Hewlett Packard</item>
</string-array>
<array name="known_print_plugin_vendors" translatable="false">
<item>@array/known_print_vendor_info_for_mopria</item>
<item>@array/known_print_vendor_info_for_hp</item>
</array>
</resources>

View File

@@ -27,4 +27,5 @@
<string name="plugin_vendor_epson">Epson</string>
<string name="plugin_vendor_konika_minolta">Konika Minolta</string>
<string name="plugin_vendor_fuji">Fuji</string>
<string name="plugin_vendor_morpia">Mopria</string>
</resources>

View File

@@ -17,16 +17,6 @@
-->
<vendors>
<vendor>
<name>@string/plugin_vendor_hp</name>
<package>com.hp.android.printservice</package>
<mdns-names>
<mdns-name>HP</mdns-name>
<mdns-name>Hewlett-Packard</mdns-name>
<mdns-name>Hewlett Packard</mdns-name>
</mdns-names>
</vendor>
<vendor>
<name>@string/plugin_vendor_lexmark</name>
<package>com.lexmark.print.plugin</package>

View File

@@ -21,8 +21,10 @@ import android.printservice.recommendation.RecommendationInfo;
import android.printservice.recommendation.RecommendationService;
import android.printservice.PrintService;
import android.util.Log;
import com.android.printservice.recommendation.plugin.hp.HPRecommendationPlugin;
import com.android.printservice.recommendation.plugin.mdnsFilter.MDNSFilterPlugin;
import com.android.printservice.recommendation.plugin.mdnsFilter.VendorConfig;
import com.android.printservice.recommendation.plugin.mopria.MopriaRecommendationPlugin;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
@@ -56,6 +58,22 @@ public class RecommendationServiceImpl extends RecommendationService
new RuntimeException("Could not parse vendorconfig", e);
}
try {
mPlugins.add(new RemotePrintServicePlugin(new HPRecommendationPlugin(this), this,
false));
} catch (Exception e) {
Log.e(LOG_TAG, "Could not initiate " + getString(R.string.plugin_vendor_hp) + " plugin",
e);
}
try {
mPlugins.add(new RemotePrintServicePlugin(new MopriaRecommendationPlugin(this), this,
true));
} catch (Exception e) {
Log.e(LOG_TAG, "Could not initiate " + getString(R.string.plugin_vendor_morpia) +
" plugin", e);
}
final int numPlugins = mPlugins.size();
for (int i = 0; i < numPlugins; i++) {
try {

View File

@@ -0,0 +1,100 @@
/*
(c) Copyright 2016 HP Inc.
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.android.printservice.recommendation.plugin.hp;
import android.content.Context;
import android.net.nsd.NsdServiceInfo;
import android.text.TextUtils;
import com.android.printservice.recommendation.R;
import java.util.Locale;
public class HPRecommendationPlugin extends ServiceRecommendationPlugin {
private static final String PDL__PCL = "application/vnd.hp-PCL";
private static final String PDL__PCLM = "application/PCLm";
private static final String PDL__PDF = "application/pdf";
private static final String PDL__PWG_RASTER = "image/pwg-raster";
private static final String TAG_DESIGNJET = "DESIGNJET";
private static final String TAG_PAGEWIDE = "PAGEWIDE";
private static final String TAG_LATEX = "LATEX";
private static final String TAG_SCITEX = "SCITEX";
private static final String TAG_XL = "XL";
private static final String ATTRIBUTE_VALUE__TRUE = "T";
private static final String MDNS_ATTRIBUTE__HPLFMOBILEPRINTER = "hplfpmobileprinter";
private static final String MDNS_ATTRIBUTE__TY = "ty";
private static String[] mSupportedDesignJet = new String[]{
"HP DESIGNJET T120",
"HP DESIGNJET T520",
"HP DESIGNJET T930",
"HP DESIGNJET T1530",
"HP DESIGNJET T2530",
"HP DESIGNJET T730",
"HP DESIGNJET T830",
};
private boolean isPrintSupported(String printerModel) {
boolean isSupported;
if (!TextUtils.isEmpty(printerModel)) {
String modelToUpper = printerModel.toUpperCase(Locale.US);
if (modelToUpper.contains(TAG_DESIGNJET)) {
isSupported = isSupportedDesignjet(printerModel);
} else
isSupported = !(modelToUpper.contains(TAG_LATEX) || modelToUpper.contains(TAG_SCITEX)) && !(modelToUpper.contains(TAG_PAGEWIDE) && modelToUpper.contains(TAG_XL));
} else {
isSupported = false;
}
return isSupported;
}
private static boolean isSupportedDesignjet(String printerModel) {
boolean isSupported = false;
if (!TextUtils.isEmpty(printerModel)) {
String modelToUpper = printerModel.toUpperCase(Locale.US);
for (String supportedPrinter : mSupportedDesignJet) {
if (modelToUpper.contains(supportedPrinter)) {
isSupported = true;
}
}
}
return isSupported;
}
public HPRecommendationPlugin(Context context) {
super(context, R.string.plugin_vendor_hp, new VendorInfo(context.getResources(), R.array.known_print_vendor_info_for_hp), new String[]{"_pdl-datastream._tcp","_ipp._tcp", "_ipps._tcp"});
}
@Override
public boolean matchesCriteria(String vendor, NsdServiceInfo nsdServiceInfo) {
if (!TextUtils.equals(vendor, mVendorInfo.mVendorID)) return false;
String pdls = MDnsUtils.getString(nsdServiceInfo.getAttributes().get(PDL_ATTRIBUTE));
boolean hasMobileSupport = TextUtils.equals(ATTRIBUTE_VALUE__TRUE, MDnsUtils.getString(nsdServiceInfo.getAttributes().get(MDNS_ATTRIBUTE__HPLFMOBILEPRINTER)));
return (((hasMobileSupport || isPrintSupported(MDnsUtils.getString(nsdServiceInfo.getAttributes().get(MDNS_ATTRIBUTE__TY))))
&&!TextUtils.isEmpty(pdls))
&& (pdls.contains(PDL__PCL)
|| pdls.contains(PDL__PDF)
|| pdls.contains(PDL__PCLM)
|| pdls.contains(PDL__PWG_RASTER)));
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.printservice.recommendation.plugin.hp;
import android.net.nsd.NsdServiceInfo;
import android.text.TextUtils;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Map;
public class MDnsUtils {
public static final String ATTRIBUTE__TY = "ty";
public static final String ATTRIBUTE__PRODUCT = "product";
public static final String ATTRIBUTE__USB_MFG = "usb_MFG";
public static final String ATTRIBUTE__MFG = "mfg";
public static String getString(byte[] value) {
if (value != null) return new String(value,StandardCharsets.UTF_8);
return null;
}
public static boolean isVendorPrinter(NsdServiceInfo networkDevice, String[] vendorValues) {
Map<String,byte[]> attributes = networkDevice.getAttributes();
String product = getString(attributes.get(ATTRIBUTE__PRODUCT));
String ty = getString(attributes.get(ATTRIBUTE__TY));
String usbMfg = getString(attributes.get(ATTRIBUTE__USB_MFG));
String mfg = getString(attributes.get(ATTRIBUTE__MFG));
return containsVendor(product, vendorValues) || containsVendor(ty, vendorValues) || containsVendor(usbMfg, vendorValues) || containsVendor(mfg, vendorValues);
}
public static String getVendor(NsdServiceInfo networkDevice) {
String vendor;
Map<String,byte[]> attributes = networkDevice.getAttributes();
vendor = getString(attributes.get(ATTRIBUTE__MFG));
if (!TextUtils.isEmpty(vendor)) return vendor;
vendor = getString(attributes.get(ATTRIBUTE__USB_MFG));
if (!TextUtils.isEmpty(vendor)) return vendor;
return null;
}
private static boolean containsVendor(String container, String[] vendorValues) {
if ((container == null) || (vendorValues == null)) return false;
for (String value : vendorValues) {
if (containsString(container, value)
|| containsString(container.toLowerCase(Locale.US), value.toLowerCase(Locale.US))
|| containsString(container.toUpperCase(Locale.US), value.toUpperCase(Locale.US)))
return true;
}
return false;
}
private static boolean containsString(String container, String contained) {
return (container != null) && (contained != null) && (container.equalsIgnoreCase(contained) || container.contains(contained + " "));
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.printservice.recommendation.plugin.hp;
import android.net.nsd.NsdServiceInfo;
import java.util.HashMap;
final class PrinterHashMap extends HashMap<String, NsdServiceInfo> {
public static String getKey(NsdServiceInfo serviceInfo) {
return serviceInfo.getServiceName();
}
public NsdServiceInfo addPrinter(NsdServiceInfo device) {
return put(getKey(device), device);
}
public NsdServiceInfo removePrinter(NsdServiceInfo device) {
return remove(getKey(device));
}
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.printservice.recommendation.plugin.hp;
import android.content.Context;
import android.content.res.TypedArray;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.text.TextUtils;
import android.util.Pair;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.android.printservice.recommendation.R;
import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer;
public class ServiceListener implements ServiceResolveQueue.ResolveCallback {
private final NsdManager mNSDManager;
private final Map<String, VendorInfo> mVendorInfoHashMap;
private final String[] mServiceType;
private final Observer mObserver;
private final ServiceResolveQueue mResolveQueue;
private List<NsdManager.DiscoveryListener> mListeners = new ArrayList<>();
public HashMap<String, PrinterHashMap> mVendorHashMap = new HashMap<>();
public interface Observer {
boolean matchesCriteria(String vendor, NsdServiceInfo serviceInfo);
void dataSetChanged();
}
public ServiceListener(Context context, Observer observer, String[] serviceTypes) {
mObserver = observer;
mServiceType = serviceTypes;
mNSDManager = (NsdManager)context.getSystemService(Context.NSD_SERVICE);
mResolveQueue = ServiceResolveQueue.getInstance(mNSDManager);
Map<String, VendorInfo> vendorInfoMap = new HashMap<>();
TypedArray testArray = context.getResources().obtainTypedArray(R.array.known_print_plugin_vendors);
for(int i = 0; i < testArray.length(); i++) {
int arrayID = testArray.getResourceId(i, 0);
if (arrayID != 0) {
VendorInfo info = new VendorInfo(context.getResources(), arrayID);
vendorInfoMap.put(info.mVendorID, info);
vendorInfoMap.put(info.mPackageName, info);
}
}
testArray.recycle();
mVendorInfoHashMap = vendorInfoMap;
}
@Override
public void serviceResolved(NsdServiceInfo nsdServiceInfo) {
printerFound(nsdServiceInfo);
}
private synchronized void printerFound(NsdServiceInfo nsdServiceInfo) {
if (nsdServiceInfo == null) return;
if (TextUtils.isEmpty(PrinterHashMap.getKey(nsdServiceInfo))) return;
String vendor = MDnsUtils.getVendor(nsdServiceInfo);
if (vendor == null) vendor = "";
for(Map.Entry<String,VendorInfo> entry : mVendorInfoHashMap.entrySet()) {
for(String vendorValues : entry.getValue().mDNSValues) {
if (vendor.equalsIgnoreCase(vendorValues)) {
vendor = entry.getValue().mVendorID;
break;
}
}
// intentional pointer check
//noinspection StringEquality
if ((vendor != entry.getValue().mVendorID) &&
MDnsUtils.isVendorPrinter(nsdServiceInfo, entry.getValue().mDNSValues)) {
vendor = entry.getValue().mVendorID;
}
// intentional pointer check
//noinspection StringEquality
if (vendor == entry.getValue().mVendorID) break;
}
if (TextUtils.isEmpty(vendor)) {
return;
}
if (!mObserver.matchesCriteria(vendor, nsdServiceInfo))
return;
boolean mapsChanged;
PrinterHashMap vendorHash = mVendorHashMap.get(vendor);
if (vendorHash == null) {
vendorHash = new PrinterHashMap();
}
mapsChanged = (vendorHash.addPrinter(nsdServiceInfo) == null);
mVendorHashMap.put(vendor, vendorHash);
if (mapsChanged) {
mObserver.dataSetChanged();
}
}
private synchronized void printerRemoved(NsdServiceInfo nsdServiceInfo) {
boolean wasRemoved = false;
Set<String> vendors = mVendorHashMap.keySet();
for(String vendor : vendors) {
PrinterHashMap map = mVendorHashMap.get(vendor);
wasRemoved |= (map.removePrinter(nsdServiceInfo) != null);
if (map.isEmpty()) wasRemoved |= (mVendorHashMap.remove(vendor) != null);
}
if (wasRemoved) {
mObserver.dataSetChanged();
}
}
public void start() {
stop();
for(final String service :mServiceType) {
NsdManager.DiscoveryListener listener = new NsdManager.DiscoveryListener() {
@Override
public void onStartDiscoveryFailed(String s, int i) {
}
@Override
public void onStopDiscoveryFailed(String s, int i) {
}
@Override
public void onDiscoveryStarted(String s) {
}
@Override
public void onDiscoveryStopped(String s) {
}
@Override
public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
mResolveQueue.queueRequest(nsdServiceInfo, ServiceListener.this);
}
@Override
public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
mResolveQueue.removeRequest(nsdServiceInfo, ServiceListener.this);
printerRemoved(nsdServiceInfo);
}
};
DiscoveryListenerMultiplexer.addListener(mNSDManager, service, listener);
mListeners.add(listener);
}
}
public void stop() {
for(NsdManager.DiscoveryListener listener : mListeners) {
DiscoveryListenerMultiplexer.removeListener(mNSDManager, listener);
}
mVendorHashMap.clear();
mListeners.clear();
}
public Pair<Integer, Integer> getCount() {
int count = 0;
for (PrinterHashMap map : mVendorHashMap.values()) {
count += map.size();
}
return Pair.create(mVendorHashMap.size(), count);
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.printservice.recommendation.plugin.hp;
import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.annotation.NonNull;
import android.text.TextUtils;
import com.android.printservice.recommendation.PrintServicePlugin;
public abstract class ServiceRecommendationPlugin implements PrintServicePlugin, ServiceListener.Observer {
protected static final String PDL_ATTRIBUTE = "pdl";
protected final Object mLock = new Object();
protected PrinterDiscoveryCallback mCallback = null;
protected final ServiceListener mListener;
protected final NsdManager mNSDManager;
protected final VendorInfo mVendorInfo;
private final int mVendorStringID;
protected ServiceRecommendationPlugin(Context context, int vendorStringID, VendorInfo vendorInfo, String[] services) {
mNSDManager = (NsdManager)context.getSystemService(Context.NSD_SERVICE);
mVendorStringID = vendorStringID;
mVendorInfo = vendorInfo;
mListener = new ServiceListener(context, this, services);
}
@Override
public int getName() {
return mVendorStringID;
}
@NonNull
@Override
public CharSequence getPackageName() {
return mVendorInfo.mPackageName;
}
@Override
public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
synchronized (mLock) {
mCallback = callback;
}
mListener.start();
}
@Override
public void stop() throws Exception {
synchronized (mLock) {
mCallback = null;
}
mListener.stop();
}
@Override
public void dataSetChanged() {
synchronized (mLock) {
if (mCallback != null) mCallback.onChanged(getCount());
}
}
@Override
public boolean matchesCriteria(String vendor, NsdServiceInfo nsdServiceInfo) {
return TextUtils.equals(vendor, mVendorInfo.mVendorID);
}
public int getCount() {
return mListener.getCount().second;
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.printservice.recommendation.plugin.hp;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.util.Pair;
import com.android.printservice.recommendation.util.NsdResolveQueue;
import java.util.LinkedList;
final class ServiceResolveQueue {
private final NsdManager mNsdManager;
private final LinkedList<Pair<NsdServiceInfo, ResolveCallback>> mQueue = new LinkedList<>();
private final Object mLock = new Object();
private static Object sLock = new Object();
private static ServiceResolveQueue sInstance = null;
private final NsdResolveQueue mNsdResolveQueue;
private Pair<NsdServiceInfo, ResolveCallback> mCurrentRequest = null;
public static void createInstance(NsdManager nsdManager) {
if (sInstance == null) sInstance = new ServiceResolveQueue(nsdManager);
}
public static ServiceResolveQueue getInstance(NsdManager nsdManager) {
synchronized (sLock) {
createInstance(nsdManager);
return sInstance;
}
}
public static void destroyInstance() {
sInstance = null;
}
public interface ResolveCallback {
void serviceResolved(NsdServiceInfo nsdServiceInfo);
}
public ServiceResolveQueue(NsdManager nsdManager) {
mNsdManager = nsdManager;
mNsdResolveQueue = NsdResolveQueue.getInstance();
}
public void queueRequest(NsdServiceInfo serviceInfo, ResolveCallback callback) {
synchronized (mLock) {
Pair<NsdServiceInfo, ResolveCallback> newRequest = Pair.create(serviceInfo, callback);
if (mQueue.contains(newRequest)) return;
mQueue.add(newRequest);
makeNextRequest();
}
}
public void removeRequest(NsdServiceInfo serviceInfo, ResolveCallback callback) {
synchronized (mLock) {
Pair<NsdServiceInfo, ResolveCallback> newRequest = Pair.create(serviceInfo, callback);
mQueue.remove(newRequest);
if ((mCurrentRequest != null) && newRequest.equals(mCurrentRequest)) mCurrentRequest = null;
}
}
private void makeNextRequest() {
synchronized (mLock) {
if (mCurrentRequest != null) return;
if (mQueue.isEmpty()) return;
mCurrentRequest = mQueue.removeFirst();
mNsdResolveQueue.resolve(mNsdManager, mCurrentRequest.first,
new NsdManager.ResolveListener() {
@Override
public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int i) {
synchronized (mLock) {
if (mCurrentRequest != null) mQueue.add(mCurrentRequest);
makeNextRequest();
}
}
@Override
public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
synchronized (mLock) {
if (mCurrentRequest != null) {
mCurrentRequest.second.serviceResolved(nsdServiceInfo);
mCurrentRequest = null;
}
makeNextRequest();
}
}
});
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.printservice.recommendation.plugin.hp;
import android.content.res.Resources;
import java.util.Arrays;
public final class VendorInfo {
public final String mPackageName;
public final String mVendorID;
public final String[] mDNSValues;
public final int mID;
public VendorInfo(Resources resources, int vendor_info_id) {
mID = vendor_info_id;
String[] data = resources.getStringArray(vendor_info_id);
if ((data == null) || (data.length < 2)) {
data = new String[] { null, null };
}
mPackageName = data[0];
mVendorID = data[1];
mDNSValues = (data.length > 2) ? Arrays.copyOfRange(data, 2, data.length) : new String[]{};
}
}

View File

@@ -26,7 +26,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.printservice.recommendation.PrintServicePlugin;
import com.android.printservice.recommendation.util.MDNSUtils;
import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer;
import com.android.printservice.recommendation.util.NsdResolveQueue;
import java.util.HashSet;
@@ -81,7 +81,7 @@ public class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.Discover
@NonNull CharSequence packageName, @NonNull List<String> mDNSNames) {
mContext = Preconditions.checkNotNull(context, "context");
mName = mContext.getResources().getIdentifier(Preconditions.checkStringNotEmpty(name,
"name"), null, mContext.getPackageName());
"name"), null, "com.android.printservice.recommendation");
mPackageName = Preconditions.checkStringNotEmpty(packageName);
mMDNSNames = new HashSet<>(Preconditions
.checkCollectionNotEmpty(Preconditions.checkCollectionElementsNotNull(mDNSNames,
@@ -107,8 +107,7 @@ public class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.Discover
public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
mCallback = callback;
getNDSManager().discoverServices(PRINTER_SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
this);
DiscoveryListenerMultiplexer.addListener(getNDSManager(), PRINTER_SERVICE_TYPE, this);
}
@Override
@@ -121,7 +120,7 @@ public class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.Discover
mCallback.onChanged(0);
mCallback = null;
getNDSManager().stopServiceDiscovery(this);
DiscoveryListenerMultiplexer.removeListener(getNDSManager(), this);
}
@Override

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
package com.android.printservice.recommendation.util;
package com.android.printservice.recommendation.plugin.mdnsFilter;
import android.annotation.NonNull;
import android.net.nsd.NsdServiceInfo;
@@ -27,7 +27,7 @@ import java.util.Set;
/**
* Utils for dealing with mDNS attributes
*/
public class MDNSUtils {
class MDNSUtils {
public static final String ATTRIBUTE_TY = "ty";
public static final String ATTRIBUTE_PRODUCT = "product";
public static final String ATTRIBUTE_USB_MFG = "usb_mfg";

View File

@@ -0,0 +1,54 @@
/*
* (c) Copyright 2016 Mopria Alliance, Inc.
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.printservice.recommendation.plugin.mopria;
import android.content.Context;
import android.net.nsd.NsdServiceInfo;
import android.text.TextUtils;
import android.util.Pair;
import com.android.printservice.recommendation.plugin.hp.MDnsUtils;
import com.android.printservice.recommendation.plugin.hp.ServiceRecommendationPlugin;
import com.android.printservice.recommendation.plugin.hp.VendorInfo;
import com.android.printservice.recommendation.R;
public class MopriaRecommendationPlugin extends ServiceRecommendationPlugin {
private static final String PDL__PDF = "application/pdf";
private static final String PDL__PCLM = "application/PCLm";
private static final String PDL__PWG_RASTER = "image/pwg-raster";
public MopriaRecommendationPlugin(Context context) {
super(context, R.string.plugin_vendor_morpia, new VendorInfo(context.getResources(), R.array.known_print_vendor_info_for_mopria), new String[]{"_ipp._tcp", "_ipps._tcp"});
}
@Override
public boolean matchesCriteria(String vendor, NsdServiceInfo nsdServiceInfo) {
String pdls = MDnsUtils.getString(nsdServiceInfo.getAttributes().get(PDL_ATTRIBUTE));
return (!TextUtils.isEmpty(pdls)
&& (pdls.contains(PDL__PDF)
|| pdls.contains(PDL__PCLM)
|| pdls.contains(PDL__PWG_RASTER)));
}
@Override
public int getCount() {
Pair<Integer, Integer> count = mListener.getCount();
return ((count.first > 1) ? count.second : 0);
}
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.printservice.recommendation.util;
import android.annotation.NonNull;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.util.ArrayMap;
import android.util.Log;
import java.util.ArrayList;
/**
* Used to multiplex listening for NSD services. This is needed as only a limited amount of
* {@link NsdManager.DiscoveryListener listeners} are allowed.
*/
public class DiscoveryListenerMultiplexer {
private static final String LOG_TAG = "DiscoveryListenerMx";
/** List of registered {@link DiscoveryListenerSet discovery sets}. */
private static final @NonNull ArrayMap<String, DiscoveryListenerSet> sListeners =
new ArrayMap<>();
/**
* Add a new {@link NsdManager.DiscoveryListener listener} for a {@code serviceType}.
*
* @param nsdManager The {@link NsdManager NSD manager} to use
* @param serviceType The service type to listen for
* @param newListener the {@link NsdManager.DiscoveryListener listener} to add.
*/
public static void addListener(@NonNull NsdManager nsdManager, @NonNull String serviceType,
@NonNull NsdManager.DiscoveryListener newListener) {
synchronized (sListeners) {
DiscoveryListenerSet listenerSet = sListeners.get(serviceType);
if (listenerSet == null) {
ArrayList<NsdManager.DiscoveryListener> subListeners = new ArrayList<>(1);
listenerSet = new DiscoveryListenerSet(subListeners,
new MultiListener(subListeners));
sListeners.put(serviceType, listenerSet);
}
synchronized (listenerSet.subListeners) {
if (listenerSet.subListeners.isEmpty()) {
nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
listenerSet.mainListener);
}
listenerSet.subListeners.add(newListener);
}
}
}
/**
* Remove a previously added {@link NsdManager.DiscoveryListener listener}.
*
* @param nsdManager The {@link NsdManager NSD manager} to use
* @param listener The {@link NsdManager.DiscoveryListener listener} that was registered
*
* @return true iff the listener was removed
*/
public static boolean removeListener(@NonNull NsdManager nsdManager,
@NonNull NsdManager.DiscoveryListener listener) {
boolean wasRemoved = false;
synchronized (sListeners) {
for (DiscoveryListenerSet listeners : sListeners.values()) {
synchronized (listeners) {
wasRemoved = listeners.subListeners.remove(listener);
if (wasRemoved) {
if (listeners.subListeners.isEmpty()) {
nsdManager.stopServiceDiscovery(listeners.mainListener);
}
break;
}
}
}
}
return wasRemoved;
}
/** Private class holding all data for a service type */
private static class DiscoveryListenerSet {
/** The plugin's listeners */
final @NonNull ArrayList<NsdManager.DiscoveryListener> subListeners;
/** The listener registered with the NSD Manager */
final @NonNull MultiListener mainListener;
private DiscoveryListenerSet(ArrayList<NsdManager.DiscoveryListener> subListeners,
MultiListener mainListener) {
this.subListeners = subListeners;
this.mainListener = mainListener;
}
}
/**
* A {@link NsdManager.DiscoveryListener} that calls a list of registered listeners when
* a service is found or lost.
*/
private static class MultiListener implements NsdManager.DiscoveryListener {
private final @NonNull ArrayList<NsdManager.DiscoveryListener> mListeners;
/**
* Create a new multi listener.
*
* @param listeners The listeners to forward the calls.
*/
public MultiListener(@NonNull ArrayList<NsdManager.DiscoveryListener> listeners) {
mListeners = listeners;
}
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
Log.w(LOG_TAG, "Failed to start network discovery for type " + serviceType + ": "
+ errorCode);
}
@Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
Log.w(LOG_TAG, "Failed to stop network discovery for type " + serviceType + ": "
+ errorCode);
}
@Override
public void onDiscoveryStarted(String serviceType) {
// not implemented
}
@Override
public void onDiscoveryStopped(String serviceType) {
// not implemented
}
@Override
public void onServiceFound(NsdServiceInfo serviceInfo) {
synchronized (mListeners) {
int numListeners = mListeners.size();
for (int i = 0; i < numListeners; i++) {
NsdManager.DiscoveryListener listener = mListeners.get(i);
listener.onServiceFound(serviceInfo);
}
}
}
@Override
public void onServiceLost(NsdServiceInfo serviceInfo) {
synchronized (mListeners) {
int numListeners = mListeners.size();
for (int i = 0; i < numListeners; i++) {
NsdManager.DiscoveryListener listener = mListeners.get(i);
listener.onServiceLost(serviceInfo);
}
}
}
}
}