/* * 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 = 100; 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 mActiveServices = new HashMap(); private final List mInstalledServices = new ArrayList(); private final Set mEnabledServices = new HashSet(); 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 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 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 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 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 onPrintersUpdated(List 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.onPrintersUpdatedLocked(printers); } } public void updateIfNeededLocked() { throwIfDestroyedLocked(); if (readConfigurationLocked()) { onConfigurationChangedLocked(); } } public RemotePrintSpooler getSpoolerLocked() { throwIfDestroyedLocked(); return mSpooler; } public Map getActiveServicesLocked() { synchronized(mLock) { throwIfDestroyedLocked(); return mActiveServices; } } public Set 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 tempPrintServices = new HashSet(); List 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 tempEnabledServiceNameSet = new HashSet(); 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 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 enabledOnFirstBoot = new HashSet(); 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 mPrinters = new ArrayMap(); private final RemoteCallbackList mDiscoveryObservers = new RemoteCallbackList() { @Override public void onCallbackDied(IPrinterDiscoveryObserver observer) { synchronized (mLock) { stopPrinterDiscoveryLocked(observer); removeObserverLocked(observer); } } }; private final List mStartedPrinterDiscoveryTokens = new ArrayList(); private final List mStateTrackedPrinters = new ArrayList(); private final Handler mHandler; private boolean mIsDestroyed; public PrinterDiscoverySessionMediator(Context context) { mHandler = new SessionHandler(context.getMainLooper()); // Kick off the session creation. List services = new ArrayList( 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 printers = new ArrayList(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 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 services = new ArrayList( 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 services = new ArrayList( mActiveServices.values()); mHandler.obtainMessage(SessionHandler .MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services) .sendToTarget(); } public void validatePrintersLocked(List printerIds) { if (mIsDestroyed) { Log.w(LOG_TAG, "Not validating pritners - session destroyed"); return; } List remainingList = new ArrayList(printerIds); while (!remainingList.isEmpty()) { Iterator iterator = remainingList.iterator(); // Gather the printers per service and request a validation. List updateList = new ArrayList(); 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 services = new ArrayList( mActiveServices.values()); mHandler.obtainMessage(SessionHandler .MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION, services) .sendToTarget(); } public void onPrintersAddedLocked(List printers) { if (DEBUG) { Log.i(LOG_TAG, "onPrintersAddedLocked()"); } if (mIsDestroyed) { Log.w(LOG_TAG, "Not adding printers - session destroyed"); return; } List addedPrinters = null; final int addedPrinterCount = printers.size(); for (int i = 0; i < addedPrinterCount; i++) { PrinterInfo printer = printers.get(i); if (!mPrinters.containsKey(printer.getId())) { mPrinters.put(printer.getId(), printer); if (addedPrinters == null) { addedPrinters = new ArrayList(); } addedPrinters.add(printer); } } if (addedPrinters != null) { mHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED, addedPrinters).sendToTarget(); } } public void onPrintersRemovedLocked(List printerIds) { if (DEBUG) { Log.i(LOG_TAG, "onPrintersRemovedLocked()"); } if (mIsDestroyed) { Log.w(LOG_TAG, "Not removing printers - session destroyed"); return; } List 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(); } removedPrinterIds.add(removedPrinterId); } } if (removedPrinterIds != null) { mHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, removedPrinterIds).sendToTarget(); } } public void onPrintersUpdatedLocked(List printers) { if (DEBUG) { Log.i(LOG_TAG, "onPrintersUpdatedLocked()"); } if (mIsDestroyed) { Log.w(LOG_TAG, "Not updating printers - session destroyed"); return; } List updatedPrinters = null; final int updatedPrinterCount = printers.size(); for (int i = 0; i < updatedPrinterCount; i++) { PrinterInfo updatedPrinter = printers.get(i); if (mPrinters.containsKey(updatedPrinter.getId())) { mPrinters.put(updatedPrinter.getId(), updatedPrinter); if (updatedPrinters == null) { updatedPrinters = new ArrayList(); } updatedPrinters.add(updatedPrinter); } } if (updatedPrinters != null) { mHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_UPDATED, updatedPrinters).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 removedPrinters = null; final int printerCount = mPrinters.size(); for (int i = 0; i < printerCount; i++) { PrinterInfo printer = mPrinters.get(i); if (printer.getId().getServiceName().equals(serviceName)) { if (removedPrinters == null) { removedPrinters = new ArrayList(); } removedPrinters.add(printer); } } if (!removedPrinters.isEmpty()) { mHandler.obtainMessage( SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, removedPrinters).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 addedPrinters) { final int observerCount = mDiscoveryObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); try { observer.onPrintersAdded(addedPrinters); } catch (RemoteException re) { Log.i(LOG_TAG, "Error dispatching added printers", re); } } mDiscoveryObservers.finishBroadcast(); } private void handleDispatchPrintersRemoved(List removedPrinterIds) { final int observerCount = mDiscoveryObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); try { observer.onPrintersRemoved(removedPrinterIds); } catch (RemoteException re) { Log.i(LOG_TAG, "Error dispatching removed printers", re); } } mDiscoveryObservers.finishBroadcast(); } private void handleDispatchPrintersUpdated(List updatedPrinters) { final int observerCount = mDiscoveryObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); try { observer.onPrintersUpdated(updatedPrinters); } catch (RemoteException re) { Log.i(LOG_TAG, "Error dispatching updated printers", re); } } mDiscoveryObservers.finishBroadcast(); } private void handleDispatchCreatePrinterDiscoverySession( List services) { final int serviceCount = services.size(); for (int i = 0; i < serviceCount; i++) { RemotePrintService service = services.get(i); service.createPrinterDiscoverySession(); } } private void handleDispatchDestroyPrinterDiscoverySession( List 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 services, List 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 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 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 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 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 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 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_DISPATCH_PRINTERS_UPDATED = 5; public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 6; public static final int MSG_START_PRINTER_DISCOVERY = 7; public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 8; public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 9; public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 10; public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 11; public static final int MSG_VALIDATE_PRINTERS = 12; public static final int MSG_START_PRINTER_STATE_TRACKING = 13; public static final int MSG_STOP_PRINTER_STATE_TRACKING = 14; 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 addedPrinters = (List) args.arg2; args.recycle(); handlePrintersAdded(observer, addedPrinters); } break; case MSG_PRINTERS_REMOVED: { SomeArgs args = (SomeArgs) message.obj; IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; List removedPrinterIds = (List) args.arg2; args.recycle(); handlePrintersRemoved(observer, removedPrinterIds); } case MSG_DISPATCH_PRINTERS_ADDED: { List addedPrinters = (List) message.obj; handleDispatchPrintersAdded(addedPrinters); } break; case MSG_DISPATCH_PRINTERS_REMOVED: { List removedPrinterIds = (List) message.obj; handleDispatchPrintersRemoved(removedPrinterIds); } break; case MSG_DISPATCH_PRINTERS_UPDATED: { List updatedPrinters = (List) message.obj; handleDispatchPrintersUpdated(updatedPrinters); } 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 services = (List) message.obj; handleDispatchCreatePrinterDiscoverySession(services); } break; case MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION: { List services = (List) message.obj; handleDispatchDestroyPrinterDiscoverySession(services); } break; case MSG_DISPATCH_START_PRINTER_DISCOVERY: { SomeArgs args = (SomeArgs) message.obj; List services = (List) args.arg1; List printerIds = (List) args.arg2; args.recycle(); handleDispatchStartPrinterDiscovery(services, printerIds); } break; case MSG_DISPATCH_STOP_PRINTER_DISCOVERY: { List services = (List) message.obj; handleDispatchStopPrinterDiscovery(services); } break; case MSG_VALIDATE_PRINTERS: { SomeArgs args = (SomeArgs) message.obj; RemotePrintService service = (RemotePrintService) args.arg1; List printerIds = (List) 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); } } } } } }