blob: b37a0d91d4bddb0ae9466b69ac0bde703a5aa15c [file] [log] [blame]
/*
* Copyright (C) 2013 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.server.print;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.print.IPrinterDiscoveryObserver;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import com.android.internal.os.SomeArgs;
import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Represents the print state for a user.
*/
final class UserState implements PrintSpoolerCallbacks {
private static final String LOG_TAG = "UserState";
private static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
private static final int MAX_ITEMS_PER_CALLBACK = 50;
private static final char COMPONENT_NAME_SEPARATOR = ':';
private final SimpleStringSplitter mStringColonSplitter =
new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
private final Intent mQueryIntent =
new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
private final Map<ComponentName, RemotePrintService> mActiveServices =
new HashMap<ComponentName, RemotePrintService>();
private final List<PrintServiceInfo> mInstalledServices =
new ArrayList<PrintServiceInfo>();
private final Set<ComponentName> mEnabledServices =
new HashSet<ComponentName>();
private final Object mLock;
private final Context mContext;
private final int mUserId;
private final RemotePrintSpooler mSpooler;
private PrinterDiscoverySessionMediator mPrinterDiscoverySession;
private boolean mDestroyed;
public UserState(Context context, int userId, Object lock) {
mContext = context;
mUserId = userId;
mLock = lock;
mSpooler = new RemotePrintSpooler(context, userId, this);
synchronized (mLock) {
enableSystemPrintServicesOnFirstBootLocked();
}
}
@Override
public void onPrintJobQueued(PrintJobInfo printJob) {
final RemotePrintService service;
synchronized (mLock) {
throwIfDestroyedLocked();
ComponentName printServiceName = printJob.getPrinterId().getServiceName();
service = mActiveServices.get(printServiceName);
}
if (service != null) {
service.onPrintJobQueued(printJob);
}
}
@Override
public void onAllPrintJobsForServiceHandled(ComponentName printService) {
final RemotePrintService service;
synchronized (mLock) {
throwIfDestroyedLocked();
service = mActiveServices.get(printService);
}
if (service != null) {
service.onAllPrintJobsHandled();
}
}
public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
throwIfDestroyedLocked();
if (mActiveServices.isEmpty()) {
return;
}
if (mPrinterDiscoverySession == null) {
// If we do not have a session, tell all service to create one.
mPrinterDiscoverySession = new PrinterDiscoverySessionMediator(mContext) {
@Override
public void onDestroyed() {
mPrinterDiscoverySession = null;
}
};
// Add the observer to the brand new session.
mPrinterDiscoverySession.addObserverLocked(observer);
} else {
// If services have created session, just add the observer.
mPrinterDiscoverySession.addObserverLocked(observer);
}
}
}
public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
// Already destroyed - nothing to do.
if (mPrinterDiscoverySession == null) {
return;
}
// Remove this observer.
mPrinterDiscoverySession.removeObserverLocked(observer);
}
}
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
List<PrinterId> printerIds) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
if (mActiveServices.isEmpty()) {
return;
}
// No session - nothing to do.
if (mPrinterDiscoverySession == null) {
return;
}
// Kick of discovery.
mPrinterDiscoverySession.startPrinterDiscoveryLocked(observer,
printerIds);
}
}
public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
if (mActiveServices.isEmpty()) {
return;
}
// No session - nothing to do.
if (mPrinterDiscoverySession == null) {
return;
}
// Kick of discovery.
mPrinterDiscoverySession.stopPrinterDiscoveryLocked(observer);
}
}
public void validatePrinters(List<PrinterId> printerIds) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
if (mActiveServices.isEmpty()) {
return;
}
// No session - nothing to do.
if (mPrinterDiscoverySession == null) {
return;
}
// Request an updated.
mPrinterDiscoverySession.validatePrintersLocked(printerIds);
}
}
public void startPrinterStateTracking(PrinterId printerId) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
if (mActiveServices.isEmpty()) {
return;
}
// No session - nothing to do.
if (mPrinterDiscoverySession == null) {
return;
}
// Request start tracking the printer.
mPrinterDiscoverySession.startPrinterStateTrackingLocked(printerId);
}
}
public void stopPrinterStateTracking(PrinterId printerId) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
if (mActiveServices.isEmpty()) {
return;
}
// No session - nothing to do.
if (mPrinterDiscoverySession == null) {
return;
}
// Request stop tracking the printer.
mPrinterDiscoverySession.stopPrinterStateTrackingLocked(printerId);
}
}
public void onPrintersAdded(List<PrinterInfo> printers) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
if (mActiveServices.isEmpty()) {
return;
}
// No session - nothing to do.
if (mPrinterDiscoverySession == null) {
return;
}
// Request an updated.
mPrinterDiscoverySession.onPrintersAddedLocked(printers);
}
}
public void onPrintersRemoved(List<PrinterId> printerIds) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
if (mActiveServices.isEmpty()) {
return;
}
// No session - nothing to do.
if (mPrinterDiscoverySession == null) {
return;
}
// Request an updated.
mPrinterDiscoverySession.onPrintersRemovedLocked(printerIds);
}
}
public void updateIfNeededLocked() {
throwIfDestroyedLocked();
if (readConfigurationLocked()) {
onConfigurationChangedLocked();
}
}
public RemotePrintSpooler getSpoolerLocked() {
throwIfDestroyedLocked();
return mSpooler;
}
public Map<ComponentName, RemotePrintService> getActiveServicesLocked() {
synchronized(mLock) {
throwIfDestroyedLocked();
return mActiveServices;
}
}
public Set<ComponentName> getEnabledServices() {
synchronized(mLock) {
throwIfDestroyedLocked();
return mEnabledServices;
}
}
public void destroyLocked() {
throwIfDestroyedLocked();
mSpooler.destroy();
for (RemotePrintService service : mActiveServices.values()) {
service.destroy();
}
mActiveServices.clear();
mInstalledServices.clear();
mEnabledServices.clear();
if (mPrinterDiscoverySession != null) {
mPrinterDiscoverySession.destroyLocked();
mPrinterDiscoverySession = null;
}
mDestroyed = true;
}
private boolean readConfigurationLocked() {
boolean somethingChanged = false;
somethingChanged |= readInstalledPrintServicesLocked();
somethingChanged |= readEnabledPrintServicesLocked();
return somethingChanged;
}
private boolean readInstalledPrintServicesLocked() {
Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
List<ResolveInfo> installedServices = mContext.getPackageManager()
.queryIntentServicesAsUser(mQueryIntent, PackageManager.GET_SERVICES
| PackageManager.GET_META_DATA, mUserId);
final int installedCount = installedServices.size();
for (int i = 0, count = installedCount; i < count; i++) {
ResolveInfo installedService = installedServices.get(i);
if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals(
installedService.serviceInfo.permission)) {
ComponentName serviceName = new ComponentName(
installedService.serviceInfo.packageName,
installedService.serviceInfo.name);
Slog.w(LOG_TAG, "Skipping print service "
+ serviceName.flattenToShortString()
+ " since it does not require permission "
+ android.Manifest.permission.BIND_PRINT_SERVICE);
continue;
}
tempPrintServices.add(PrintServiceInfo.create(installedService, mContext));
}
if (!tempPrintServices.equals(mInstalledServices)) {
mInstalledServices.clear();
mInstalledServices.addAll(tempPrintServices);
return true;
}
return false;
}
private boolean readEnabledPrintServicesLocked() {
Set<ComponentName> tempEnabledServiceNameSet = new HashSet<ComponentName>();
readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
tempEnabledServiceNameSet);
if (!tempEnabledServiceNameSet.equals(mEnabledServices)) {
mEnabledServices.clear();
mEnabledServices.addAll(tempEnabledServiceNameSet);
return true;
}
return false;
}
private void readPrintServicesFromSettingLocked(String setting,
Set<ComponentName> outServiceNames) {
String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
setting, mUserId);
if (!TextUtils.isEmpty(settingValue)) {
TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
splitter.setString(settingValue);
while (splitter.hasNext()) {
String string = splitter.next();
if (TextUtils.isEmpty(string)) {
continue;
}
ComponentName componentName = ComponentName.unflattenFromString(string);
if (componentName != null) {
outServiceNames.add(componentName);
}
}
}
}
private void enableSystemPrintServicesOnFirstBootLocked() {
// Load enabled and installed services.
readEnabledPrintServicesLocked();
readInstalledPrintServicesLocked();
// Load the system services once enabled on first boot.
Set<ComponentName> enabledOnFirstBoot = new HashSet<ComponentName>();
readPrintServicesFromSettingLocked(
Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES,
enabledOnFirstBoot);
StringBuilder builder = new StringBuilder();
final int serviceCount = mInstalledServices.size();
for (int i = 0; i < serviceCount; i++) {
ServiceInfo serviceInfo = mInstalledServices.get(i).getResolveInfo().serviceInfo;
// Enable system print services if we never did that and are not enabled.
if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
ComponentName serviceName = new ComponentName(
serviceInfo.packageName, serviceInfo.name);
if (!mEnabledServices.contains(serviceName)
&& !enabledOnFirstBoot.contains(serviceName)) {
if (builder.length() > 0) {
builder.append(":");
}
builder.append(serviceName.flattenToString());
}
}
}
// Nothing to be enabled - done.
if (builder.length() <= 0) {
return;
}
String servicesToEnable = builder.toString();
// Update the enabled services setting.
String enabledServices = Settings.Secure.getStringForUser(
mContext.getContentResolver(), Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
if (TextUtils.isEmpty(enabledServices)) {
enabledServices = servicesToEnable;
} else {
enabledServices = enabledServices + ":" + servicesToEnable;
}
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.ENABLED_PRINT_SERVICES, enabledServices, mUserId);
// Update the enabled on first boot services setting.
String enabledOnFirstBootServices = Settings.Secure.getStringForUser(
mContext.getContentResolver(),
Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES, mUserId);
if (TextUtils.isEmpty(enabledOnFirstBootServices)) {
enabledOnFirstBootServices = servicesToEnable;
} else {
enabledOnFirstBootServices = enabledOnFirstBootServices + ":" + enabledServices;
}
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES,
enabledOnFirstBootServices, mUserId);
}
private void onConfigurationChangedLocked() {
final int installedCount = mInstalledServices.size();
for (int i = 0; i < installedCount; i++) {
ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo();
ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName,
resolveInfo.serviceInfo.name);
if (mEnabledServices.contains(serviceName)) {
if (!mActiveServices.containsKey(serviceName)) {
RemotePrintService service = new RemotePrintService(
mContext, serviceName, mUserId, mSpooler, this);
mActiveServices.put(serviceName, service);
if (mPrinterDiscoverySession != null) {
mPrinterDiscoverySession.onServiceAddedLocked(service);
}
}
} else {
RemotePrintService service = mActiveServices.remove(serviceName);
if (service != null) {
service.destroy();
if (mPrinterDiscoverySession != null) {
mPrinterDiscoverySession.onServiceRemovedLocked(serviceName);
}
}
}
}
}
private void throwIfDestroyedLocked() {
if (mDestroyed) {
throw new IllegalStateException("Cannot interact with a destroyed instance.");
}
}
private class PrinterDiscoverySessionMediator {
private final ArrayMap<PrinterId, PrinterInfo> mPrinters =
new ArrayMap<PrinterId, PrinterInfo>();
private final RemoteCallbackList<IPrinterDiscoveryObserver> mDiscoveryObservers =
new RemoteCallbackList<IPrinterDiscoveryObserver>() {
@Override
public void onCallbackDied(IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
stopPrinterDiscoveryLocked(observer);
removeObserverLocked(observer);
}
}
};
private final List<IBinder> mStartedPrinterDiscoveryTokens = new ArrayList<IBinder>();
private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>();
private final Handler mHandler;
private boolean mIsDestroyed;
public PrinterDiscoverySessionMediator(Context context) {
mHandler = new SessionHandler(context.getMainLooper());
// Kick off the session creation.
List<RemotePrintService> services = new ArrayList<RemotePrintService>(
mActiveServices.values());
mHandler.obtainMessage(SessionHandler
.MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION, services)
.sendToTarget();
}
public void addObserverLocked(IPrinterDiscoveryObserver observer) {
// Add the observer.
mDiscoveryObservers.register(observer);
// Bring the added observer up to speed with the printers.
if (!mPrinters.isEmpty()) {
List<PrinterInfo> printers = new ArrayList<PrinterInfo>(mPrinters.values());
SomeArgs args = SomeArgs.obtain();
args.arg1 = observer;
args.arg2 = printers;
mHandler.obtainMessage(SessionHandler.MSG_PRINTERS_ADDED,
args).sendToTarget();
}
}
public void removeObserverLocked(IPrinterDiscoveryObserver observer) {
// Remove the observer.
mDiscoveryObservers.unregister(observer);
// No one else observing - then kill it.
if (mDiscoveryObservers.getRegisteredCallbackCount() == 0) {
destroyLocked();
}
}
public final void startPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer,
List<PrinterId> priorityList) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not starting dicovery - session destroyed");
return;
}
// If printer discovery is ongoing and the start request has a list
// of printer to be checked, then we just request validating them.
if (!mStartedPrinterDiscoveryTokens.isEmpty()
&& priorityList != null && !priorityList.isEmpty()) {
validatePrinters(priorityList);
return;
}
// Remember we got a start request to match with an end.
mStartedPrinterDiscoveryTokens.add(observer.asBinder());
// The service are already performing discovery - nothing to do.
if (mStartedPrinterDiscoveryTokens.size() > 1) {
return;
}
List<RemotePrintService> services = new ArrayList<RemotePrintService>(
mActiveServices.values());
SomeArgs args = SomeArgs.obtain();
args.arg1 = services;
args.arg2 = priorityList;
mHandler.obtainMessage(SessionHandler
.MSG_DISPATCH_START_PRINTER_DISCOVERY, args)
.sendToTarget();
}
public final void stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not stopping dicovery - session destroyed");
return;
}
// This one did not make an active discovery request - nothing to do.
if (!mStartedPrinterDiscoveryTokens.remove(observer.asBinder())) {
return;
}
// There are other interested observers - do not stop discovery.
if (!mStartedPrinterDiscoveryTokens.isEmpty()) {
return;
}
List<RemotePrintService> services = new ArrayList<RemotePrintService>(
mActiveServices.values());
mHandler.obtainMessage(SessionHandler
.MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services)
.sendToTarget();
}
public void validatePrintersLocked(List<PrinterId> printerIds) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not validating pritners - session destroyed");
return;
}
List<PrinterId> remainingList = new ArrayList<PrinterId>(printerIds);
while (!remainingList.isEmpty()) {
Iterator<PrinterId> iterator = remainingList.iterator();
// Gather the printers per service and request a validation.
List<PrinterId> updateList = new ArrayList<PrinterId>();
ComponentName serviceName = null;
while (iterator.hasNext()) {
PrinterId printerId = iterator.next();
if (updateList.isEmpty()) {
updateList.add(printerId);
serviceName = printerId.getServiceName();
iterator.remove();
} else if (printerId.getServiceName().equals(serviceName)) {
updateList.add(printerId);
iterator.remove();
}
}
// Schedule a notification of the service.
RemotePrintService service = mActiveServices.get(serviceName);
if (service != null) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = service;
args.arg2 = updateList;
mHandler.obtainMessage(SessionHandler
.MSG_VALIDATE_PRINTERS, args)
.sendToTarget();
}
}
}
public final void startPrinterStateTrackingLocked(PrinterId printerId) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed");
return;
}
// If printer discovery is not started - nothing to do.
if (mStartedPrinterDiscoveryTokens.isEmpty()) {
return;
}
final boolean containedPrinterId = mStateTrackedPrinters.contains(printerId);
// Keep track of the number of requests to track this one.
mStateTrackedPrinters.add(printerId);
// If we were tracking this printer - nothing to do.
if (containedPrinterId) {
return;
}
// No service - nothing to do.
RemotePrintService service = mActiveServices.get(printerId.getServiceName());
if (service == null) {
return;
}
// Ask the service to start tracking.
SomeArgs args = SomeArgs.obtain();
args.arg1 = service;
args.arg2 = printerId;
mHandler.obtainMessage(SessionHandler
.MSG_START_PRINTER_STATE_TRACKING, args)
.sendToTarget();
}
public final void stopPrinterStateTrackingLocked(PrinterId printerId) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not stopping printer state tracking - session destroyed");
return;
}
// If printer discovery is not started - nothing to do.
if (mStartedPrinterDiscoveryTokens.isEmpty()) {
return;
}
// If we did not track this printer - nothing to do.
if (!mStateTrackedPrinters.remove(printerId)) {
return;
}
// No service - nothing to do.
RemotePrintService service = mActiveServices.get(printerId.getServiceName());
if (service == null) {
return;
}
// Ask the service to start tracking.
SomeArgs args = SomeArgs.obtain();
args.arg1 = service;
args.arg2 = printerId;
mHandler.obtainMessage(SessionHandler
.MSG_STOP_PRINTER_STATE_TRACKING, args)
.sendToTarget();
}
public void onDestroyed() {
/* do nothing */
}
public void destroyLocked() {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not destroying - session destroyed");
return;
}
// Make sure printer tracking is stopped.
final int printerCount = mStateTrackedPrinters.size();
for (int i = 0; i < printerCount; i++) {
PrinterId printerId = mStateTrackedPrinters.get(i);
stopPrinterStateTracking(printerId);
}
// Make sure discovery is stopped.
final int observerCount = mStartedPrinterDiscoveryTokens.size();
for (int i = 0; i < observerCount; i++) {
IBinder token = mStartedPrinterDiscoveryTokens.get(i);
stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver.Stub.asInterface(token));
}
// Tell the services we are done.
List<RemotePrintService> services = new ArrayList<RemotePrintService>(
mActiveServices.values());
mHandler.obtainMessage(SessionHandler
.MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION, services)
.sendToTarget();
}
public void onPrintersAddedLocked(List<PrinterInfo> printers) {
if (DEBUG) {
Log.i(LOG_TAG, "onPrintersAddedLocked()");
}
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not adding printers - session destroyed");
return;
}
List<PrinterInfo> addedPrinters = null;
final int addedPrinterCount = printers.size();
for (int i = 0; i < addedPrinterCount; i++) {
PrinterInfo printer = printers.get(i);
PrinterInfo oldPrinter = mPrinters.put(printer.getId(), printer);
if (oldPrinter == null || !oldPrinter.equals(printer)) {
if (addedPrinters == null) {
addedPrinters = new ArrayList<PrinterInfo>();
}
addedPrinters.add(printer);
}
}
if (addedPrinters != null) {
mHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED,
addedPrinters).sendToTarget();
}
}
public void onPrintersRemovedLocked(List<PrinterId> printerIds) {
if (DEBUG) {
Log.i(LOG_TAG, "onPrintersRemovedLocked()");
}
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not removing printers - session destroyed");
return;
}
List<PrinterId> removedPrinterIds = null;
final int removedPrinterCount = printerIds.size();
for (int i = 0; i < removedPrinterCount; i++) {
PrinterId removedPrinterId = printerIds.get(i);
if (mPrinters.remove(removedPrinterId) != null) {
if (removedPrinterIds == null) {
removedPrinterIds = new ArrayList<PrinterId>();
}
removedPrinterIds.add(removedPrinterId);
}
}
if (removedPrinterIds != null) {
mHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED,
removedPrinterIds).sendToTarget();
}
}
public void onServiceRemovedLocked(ComponentName serviceName) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not updating removed service - session destroyed");
return;
}
// No printers - nothing to do.
if (mPrinters.isEmpty()) {
return;
}
// Remove the printers for that service.
List<PrinterId> removedPrinterIds = null;
final int printerCount = mPrinters.size();
for (int i = 0; i < printerCount; i++) {
PrinterId printerId = mPrinters.keyAt(i);
if (printerId.getServiceName().equals(serviceName)) {
if (removedPrinterIds == null) {
removedPrinterIds = new ArrayList<PrinterId>();
}
removedPrinterIds.add(printerId);
}
}
if (!removedPrinterIds.isEmpty()) {
mHandler.obtainMessage(
SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED,
removedPrinterIds).sendToTarget();
}
}
public void onServiceAddedLocked(RemotePrintService service) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not updating added service - session destroyed");
return;
}
// Tell the service to create a session.
mHandler.obtainMessage(
SessionHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION,
service).sendToTarget();
// If there are some observers that started discovery - tell the service.
if (mDiscoveryObservers.getRegisteredCallbackCount() > 0) {
mHandler.obtainMessage(
SessionHandler.MSG_START_PRINTER_DISCOVERY,
service).sendToTarget();
}
}
private void handleDispatchPrintersAdded(List<PrinterInfo> addedPrinters) {
final int observerCount = mDiscoveryObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
handlePrintersAdded(observer, addedPrinters);
}
mDiscoveryObservers.finishBroadcast();
}
private void handleDispatchPrintersRemoved(List<PrinterId> removedPrinterIds) {
final int observerCount = mDiscoveryObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
handlePrintersRemoved(observer, removedPrinterIds);
}
mDiscoveryObservers.finishBroadcast();
}
private void handleDispatchCreatePrinterDiscoverySession(
List<RemotePrintService> services) {
final int serviceCount = services.size();
for (int i = 0; i < serviceCount; i++) {
RemotePrintService service = services.get(i);
service.createPrinterDiscoverySession();
}
}
private void handleDispatchDestroyPrinterDiscoverySession(
List<RemotePrintService> services) {
final int serviceCount = services.size();
for (int i = 0; i < serviceCount; i++) {
RemotePrintService service = services.get(i);
service.destroyPrinterDiscoverySession();
}
onDestroyed();
}
private void handleDispatchStartPrinterDiscovery(
List<RemotePrintService> services, List<PrinterId> printerIds) {
final int serviceCount = services.size();
for (int i = 0; i < serviceCount; i++) {
RemotePrintService service = services.get(i);
service.startPrinterDiscovery(printerIds);
}
}
private void handleDispatchStopPrinterDiscovery(List<RemotePrintService> services) {
final int serviceCount = services.size();
for (int i = 0; i < serviceCount; i++) {
RemotePrintService service = services.get(i);
service.stopPrinterDiscovery();
}
}
private void handleValidatePrinters(RemotePrintService service,
List<PrinterId> printerIds) {
service.validatePrinters(printerIds);
}
private void handleStartPrinterStateTracking(RemotePrintService service,
PrinterId printerId) {
service.startPrinterStateTracking(printerId);
}
private void handleStopPrinterStateTracking(RemotePrintService service,
PrinterId printerId) {
service.stopPrinterStateTracking(printerId);
}
private void handlePrintersAdded(IPrinterDiscoveryObserver observer,
List<PrinterInfo> printers) {
try {
final int printerCount = printers.size();
if (printerCount <= MAX_ITEMS_PER_CALLBACK) {
observer.onPrintersAdded(printers);
} else {
// Send the added printers in chunks avoiding the binder transaction limit.
final int transactionCount = (printerCount / MAX_ITEMS_PER_CALLBACK) + 1;
for (int i = 0; i < transactionCount; i++) {
final int start = i * MAX_ITEMS_PER_CALLBACK;
final int end = Math.min(start + MAX_ITEMS_PER_CALLBACK, printerCount);
List<PrinterInfo> subPrinters = printers.subList(start, end);
observer.onPrintersAdded(subPrinters);
}
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error sending added printers", re);
}
}
private void handlePrintersRemoved(IPrinterDiscoveryObserver observer,
List<PrinterId> printerIds) {
try {
final int printerCount = printerIds.size();
if (printerCount <= MAX_ITEMS_PER_CALLBACK) {
observer.onPrintersRemoved(printerIds);
} else {
// Send the added printers in chunks avoiding the binder transaction limit.
final int transactionCount = (printerCount / MAX_ITEMS_PER_CALLBACK) + 1;
for (int i = 0; i < transactionCount; i++) {
final int start = i * MAX_ITEMS_PER_CALLBACK;
final int end = Math.min(start + MAX_ITEMS_PER_CALLBACK, printerCount);
List<PrinterId> subPrinterIds = printerIds.subList(start, end);
observer.onPrintersRemoved(subPrinterIds);
}
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error sending added printers", re);
}
}
private final class SessionHandler extends Handler {
public static final int MSG_PRINTERS_ADDED = 1;
public static final int MSG_PRINTERS_REMOVED = 2;
public static final int MSG_DISPATCH_PRINTERS_ADDED = 3;
public static final int MSG_DISPATCH_PRINTERS_REMOVED = 4;
public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 5;
public static final int MSG_START_PRINTER_DISCOVERY = 6;
public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 7;
public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 8;
public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 9;
public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 10;
public static final int MSG_VALIDATE_PRINTERS = 11;
public static final int MSG_START_PRINTER_STATE_TRACKING = 12;
public static final int MSG_STOP_PRINTER_STATE_TRACKING = 13;
SessionHandler(Looper looper) {
super(looper, null, false);
}
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message message) {
switch (message.what) {
case MSG_PRINTERS_ADDED: {
SomeArgs args = (SomeArgs) message.obj;
IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1;
List<PrinterInfo> addedPrinters = (List<PrinterInfo>) args.arg2;
args.recycle();
handlePrintersAdded(observer, addedPrinters);
} break;
case MSG_PRINTERS_REMOVED: {
SomeArgs args = (SomeArgs) message.obj;
IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1;
List<PrinterId> removedPrinterIds = (List<PrinterId>) args.arg2;
args.recycle();
handlePrintersRemoved(observer, removedPrinterIds);
}
case MSG_DISPATCH_PRINTERS_ADDED: {
List<PrinterInfo> addedPrinters = (List<PrinterInfo>) message.obj;
handleDispatchPrintersAdded(addedPrinters);
} break;
case MSG_DISPATCH_PRINTERS_REMOVED: {
List<PrinterId> removedPrinterIds = (List<PrinterId>) message.obj;
handleDispatchPrintersRemoved(removedPrinterIds);
} break;
case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
RemotePrintService service = (RemotePrintService) message.obj;
service.createPrinterDiscoverySession();
} break;
case MSG_START_PRINTER_DISCOVERY: {
RemotePrintService service = (RemotePrintService) message.obj;
service.startPrinterDiscovery(null);
} break;
case MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION: {
List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
handleDispatchCreatePrinterDiscoverySession(services);
} break;
case MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION: {
List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
handleDispatchDestroyPrinterDiscoverySession(services);
} break;
case MSG_DISPATCH_START_PRINTER_DISCOVERY: {
SomeArgs args = (SomeArgs) message.obj;
List<RemotePrintService> services = (List<RemotePrintService>) args.arg1;
List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
args.recycle();
handleDispatchStartPrinterDiscovery(services, printerIds);
} break;
case MSG_DISPATCH_STOP_PRINTER_DISCOVERY: {
List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
handleDispatchStopPrinterDiscovery(services);
} break;
case MSG_VALIDATE_PRINTERS: {
SomeArgs args = (SomeArgs) message.obj;
RemotePrintService service = (RemotePrintService) args.arg1;
List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
args.recycle();
handleValidatePrinters(service, printerIds);
} break;
case MSG_START_PRINTER_STATE_TRACKING: {
SomeArgs args = (SomeArgs) message.obj;
RemotePrintService service = (RemotePrintService) args.arg1;
PrinterId printerId = (PrinterId) args.arg2;
args.recycle();
handleStartPrinterStateTracking(service, printerId);
} break;
case MSG_STOP_PRINTER_STATE_TRACKING: {
SomeArgs args = (SomeArgs) message.obj;
RemotePrintService service = (RemotePrintService) args.arg1;
PrinterId printerId = (PrinterId) args.arg2;
args.recycle();
handleStopPrinterStateTracking(service, printerId);
}
}
}
}
}
}