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:
committed by
Android (Google) Code Review
commit
747b3d7de8
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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 + " "));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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[]{};
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user