FileDocCategorySizeDatePackage
PrintSpoolerService.javaAPI DocAndroid 5.1 API56381Thu Mar 12 22:22:42 GMT 2015com.android.printspooler.model

PrintSpoolerService

public final class PrintSpoolerService extends android.app.Service
Service for exposing some of the {@link PrintSpooler} functionality to another process.

Fields Summary
private static final String
LOG_TAG
private static final boolean
DEBUG_PRINT_JOB_LIFECYCLE
private static final boolean
DEBUG_PERSISTENCE
private static final boolean
PERSISTENCE_MANAGER_ENABLED
private static final long
CHECK_ALL_PRINTJOBS_HANDLED_DELAY
private static final String
PRINT_JOB_FILE_PREFIX
private static final String
PRINT_FILE_EXTENSION
private static final Object
sLock
private final Object
mLock
private final List
mPrintJobs
private static PrintSpoolerService
sInstance
private android.print.IPrintSpoolerClient
mClient
private com.android.internal.os.HandlerCaller
mHandlerCaller
private PersistenceManager
mPersistanceManager
private NotificationController
mNotificationController
Constructors Summary
Methods Summary
private voidaddPrintJobLocked(android.print.PrintJobInfo printJob)

        mPrintJobs.add(printJob);
        if (DEBUG_PRINT_JOB_LIFECYCLE) {
            Slog.i(LOG_TAG, "[ADD] " + printJob);
        }
    
public voidcheckAllPrintJobsHandled()

        synchronized (mLock) {
            if (!hasActivePrintJobsLocked()) {
                notifyOnAllPrintJobsHandled();
            }
        }
    
public voidcreatePrintJob(android.print.PrintJobInfo printJob)

        synchronized (mLock) {
            addPrintJobLocked(printJob);
            setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);

            Message message = mHandlerCaller.obtainMessageO(
                    HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
                    printJob);
            mHandlerCaller.executeOrSendMessage(message);
        }
    
protected voiddump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)

        synchronized (mLock) {
            String prefix = (args.length > 0) ? args[0] : "";
            String tab = "  ";

            pw.append(prefix).append("print jobs:").println();
            final int printJobCount = mPrintJobs.size();
            for (int i = 0; i < printJobCount; i++) {
                PrintJobInfo printJob = mPrintJobs.get(i);
                pw.append(prefix).append(tab).append(printJob.toString());
                pw.println();
            }

            pw.append(prefix).append("print job files:").println();
            File[] files = getFilesDir().listFiles();
            if (files != null) {
                final int fileCount = files.length;
                for (int i = 0; i < fileCount; i++) {
                    File file = files[i];
                    if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
                        pw.append(prefix).append(tab).append(file.getName()).println();
                    }
                }
            }
        }
    
public static java.io.FilegenerateFileForPrintJob(android.content.Context context, android.print.PrintJobId printJobId)

        return new File(context.getFilesDir(), PRINT_JOB_FILE_PREFIX
                + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION);
    
public android.print.PrintJobInfogetPrintJobInfo(android.print.PrintJobId printJobId, int appId)

        synchronized (mLock) {
            final int printJobCount = mPrintJobs.size();
            for (int i = 0; i < printJobCount; i++) {
                PrintJobInfo printJob = mPrintJobs.get(i);
                if (printJob.getId().equals(printJobId)
                        && (appId == PrintManager.APP_ID_ANY
                        || appId == printJob.getAppId())) {
                    return printJob;
                }
            }
            return null;
        }
    
public java.util.ListgetPrintJobInfos(android.content.ComponentName componentName, int state, int appId)

        List<PrintJobInfo> foundPrintJobs = null;
        synchronized (mLock) {
            final int printJobCount = mPrintJobs.size();
            for (int i = 0; i < printJobCount; i++) {
                PrintJobInfo printJob = mPrintJobs.get(i);
                PrinterId printerId = printJob.getPrinterId();
                final boolean sameComponent = (componentName == null
                        || (printerId != null
                        && componentName.equals(printerId.getServiceName())));
                final boolean sameAppId = appId == PrintManager.APP_ID_ANY
                        || printJob.getAppId() == appId;
                final boolean sameState = (state == printJob.getState())
                        || (state == PrintJobInfo.STATE_ANY)
                        || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
                            && isStateVisibleToUser(printJob.getState()))
                        || (state == PrintJobInfo.STATE_ANY_ACTIVE
                            && isActiveState(printJob.getState()))
                        || (state == PrintJobInfo.STATE_ANY_SCHEDULED
                            && isScheduledState(printJob.getState()));
                if (sameComponent && sameAppId && sameState) {
                    if (foundPrintJobs == null) {
                        foundPrintJobs = new ArrayList<>();
                    }
                    foundPrintJobs.add(printJob);
                }
            }
        }
        return foundPrintJobs;
    
private voidhandleReadPrintJobsLocked()

        // Make a map with the files for a print job since we may have
        // to delete some. One example of getting orphan files if the
        // spooler crashes while constructing a print job. We do not
        // persist partially populated print jobs under construction to
        // avoid special handling for various attributes missing.
        ArrayMap<PrintJobId, File> fileForJobMap = null;
        File[] files = getFilesDir().listFiles();
        if (files != null) {
            final int fileCount = files.length;
            for (int i = 0; i < fileCount; i++) {
                File file = files[i];
                if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
                    if (fileForJobMap == null) {
                        fileForJobMap = new ArrayMap<PrintJobId, File>();
                    }
                    String printJobIdString = file.getName().substring(
                            PRINT_JOB_FILE_PREFIX.length(),
                            file.getName().indexOf('."));
                    PrintJobId printJobId = PrintJobId.unflattenFromString(
                            printJobIdString);
                    fileForJobMap.put(printJobId, file);
                }
            }
        }

        final int printJobCount = mPrintJobs.size();
        for (int i = 0; i < printJobCount; i++) {
            PrintJobInfo printJob = mPrintJobs.get(i);

            // We want to have only the orphan files at the end.
            if (fileForJobMap != null) {
                fileForJobMap.remove(printJob.getId());
            }

            switch (printJob.getState()) {
                case PrintJobInfo.STATE_QUEUED:
                case PrintJobInfo.STATE_STARTED:
                case PrintJobInfo.STATE_BLOCKED: {
                    // We have a print job that was queued or started or blocked in
                    // the past but the device battery died or a crash occurred. In
                    // this case we assume the print job failed and let the user
                    // decide whether to restart the job or just cancel it.
                    setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
                            getString(R.string.no_connection_to_printer));
                } break;
            }
        }

        if (!mPrintJobs.isEmpty()) {
            // Update the notification.
            mNotificationController.onUpdateNotifications(mPrintJobs);
        }

        // Delete the orphan files.
        if (fileForJobMap != null) {
            final int orphanFileCount = fileForJobMap.size();
            for (int i = 0; i < orphanFileCount; i++) {
                File file = fileForJobMap.valueAt(i);
                file.delete();
            }
        }
    
public booleanhasActivePrintJobsForServiceLocked(android.content.ComponentName service)

        final int printJobCount = mPrintJobs.size();
        for (int i = 0; i < printJobCount; i++) {
            PrintJobInfo printJob = mPrintJobs.get(i);
            if (isActiveState(printJob.getState()) && printJob.getPrinterId() != null
                    && printJob.getPrinterId().getServiceName().equals(service)) {
                return true;
            }
        }
        return false;
    
public booleanhasActivePrintJobsLocked()

        final int printJobCount = mPrintJobs.size();
        for (int i = 0; i < printJobCount; i++) {
            PrintJobInfo printJob = mPrintJobs.get(i);
            if (isActiveState(printJob.getState())) {
                return true;
            }
        }
        return false;
    
private booleanisActiveState(int printJobState)

        return printJobState == PrintJobInfo.STATE_CREATED
                || printJobState == PrintJobInfo.STATE_QUEUED
                || printJobState == PrintJobInfo.STATE_STARTED
                || printJobState == PrintJobInfo.STATE_BLOCKED;
    
private booleanisObsoleteState(int printJobState)

        return (isTerminalState(printJobState)
                || printJobState == PrintJobInfo.STATE_QUEUED);
    
private booleanisScheduledState(int printJobState)

        return printJobState == PrintJobInfo.STATE_QUEUED
                || printJobState == PrintJobInfo.STATE_STARTED
                || printJobState == PrintJobInfo.STATE_BLOCKED;
    
private booleanisStateVisibleToUser(int state)

        return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED
                || state == PrintJobInfo.STATE_COMPLETED || state == PrintJobInfo.STATE_CANCELED
                || state == PrintJobInfo.STATE_BLOCKED));
    
private booleanisTerminalState(int printJobState)

        return printJobState == PrintJobInfo.STATE_COMPLETED
                || printJobState == PrintJobInfo.STATE_CANCELED;
    
private voidnotifyOnAllPrintJobsHandled()

        // This has to run on the tread that is persisting the current state
        // since this call may result in the system unbinding from the spooler
        // and as a result the spooler process may get killed before the write
        // completes.
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                sendOnAllPrintJobsHandled();
                return null;
            }
        }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
    
public android.os.IBinderonBind(android.content.Intent intent)

        return new PrintSpooler();
    
public voidonCreate()

        super.onCreate();
        mHandlerCaller = new HandlerCaller(this, getMainLooper(),
                new HandlerCallerCallback(), false);

        mPersistanceManager = new PersistenceManager();
        mNotificationController = new NotificationController(PrintSpoolerService.this);

        synchronized (mLock) {
            mPersistanceManager.readStateLocked();
            handleReadPrintJobsLocked();
        }

        synchronized (sLock) {
            sInstance = this;
        }
    
public static com.android.printspooler.model.PrintSpoolerServicepeekInstance()


        
        synchronized (sLock) {
            return sInstance;
        }
    
private voidremoveObsoletePrintJobs()

        synchronized (mLock) {
            boolean persistState = false;
            final int printJobCount = mPrintJobs.size();
            for (int i = printJobCount - 1; i >= 0; i--) {
                PrintJobInfo printJob = mPrintJobs.get(i);
                if (isObsoleteState(printJob.getState())) {
                    mPrintJobs.remove(i);
                    if (DEBUG_PRINT_JOB_LIFECYCLE) {
                        Slog.i(LOG_TAG, "[REMOVE] " + printJob.getId().flattenToString());
                    }
                    removePrintJobFileLocked(printJob.getId());
                    persistState = true;
                }
            }
            if (persistState) {
                mPersistanceManager.writeStateLocked();
            }
        }
    
private voidremovePrintJobFileLocked(android.print.PrintJobId printJobId)

        File file = generateFileForPrintJob(PrintSpoolerService.this, printJobId);
        if (file.exists()) {
            file.delete();
            if (DEBUG_PRINT_JOB_LIFECYCLE) {
                Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId);
            }
        }
    
private voidsendOnAllPrintJobsForServiceHandled(android.content.ComponentName service)

        Message message = mHandlerCaller.obtainMessageO(
                HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service);
        mHandlerCaller.executeOrSendMessage(message);
    
private voidsendOnAllPrintJobsHandled()

        Message message = mHandlerCaller.obtainMessage(
                HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED);
        mHandlerCaller.executeOrSendMessage(message);
    
private voidsendOnPrintJobQueued(android.print.PrintJobInfo printJob)

        Message message = mHandlerCaller.obtainMessageO(
                HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
        mHandlerCaller.executeOrSendMessage(message);
    
public voidsetPrintJobCancelling(android.print.PrintJobId printJobId, boolean cancelling)

        synchronized (mLock) {
            PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
            if (printJob != null) {
                printJob.setCancelling(cancelling);
                if (shouldPersistPrintJob(printJob)) {
                    mPersistanceManager.writeStateLocked();
                }
                mNotificationController.onUpdateNotifications(mPrintJobs);

                Message message = mHandlerCaller.obtainMessageO(
                        HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
                        printJob);
                mHandlerCaller.executeOrSendMessage(message);
            }
        }
    
public booleansetPrintJobState(android.print.PrintJobId printJobId, int state, java.lang.String error)

        boolean success = false;

        synchronized (mLock) {
            PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
            if (printJob != null) {
                final int oldState = printJob.getState();
                if (oldState == state) {
                    return false;
                }

                success = true;

                printJob.setState(state);
                printJob.setStateReason(error);
                printJob.setCancelling(false);

                if (DEBUG_PRINT_JOB_LIFECYCLE) {
                    Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
                }

                switch (state) {
                    case PrintJobInfo.STATE_COMPLETED:
                    case PrintJobInfo.STATE_CANCELED:
                        mPrintJobs.remove(printJob);
                        removePrintJobFileLocked(printJob.getId());
                        // $fall-through$

                    case PrintJobInfo.STATE_FAILED: {
                        PrinterId printerId = printJob.getPrinterId();
                        if (printerId != null) {
                            ComponentName service = printerId.getServiceName();
                            if (!hasActivePrintJobsForServiceLocked(service)) {
                                sendOnAllPrintJobsForServiceHandled(service);
                            }
                        }
                    } break;

                    case PrintJobInfo.STATE_QUEUED: {
                        sendOnPrintJobQueued(new PrintJobInfo(printJob));
                    }  break;
                }

                if (shouldPersistPrintJob(printJob)) {
                    mPersistanceManager.writeStateLocked();
                }

                if (!hasActivePrintJobsLocked()) {
                    notifyOnAllPrintJobsHandled();
                }

                Message message = mHandlerCaller.obtainMessageO(
                        HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
                        printJob);
                mHandlerCaller.executeOrSendMessage(message);

                mNotificationController.onUpdateNotifications(mPrintJobs);
            }
        }

        return success;
    
public booleansetPrintJobTag(android.print.PrintJobId printJobId, java.lang.String tag)

        synchronized (mLock) {
            PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
            if (printJob != null) {
                String printJobTag = printJob.getTag();
                if (printJobTag == null) {
                    if (tag == null) {
                        return false;
                    }
                } else if (printJobTag.equals(tag)) {
                    return false;
                }
                printJob.setTag(tag);
                if (shouldPersistPrintJob(printJob)) {
                    mPersistanceManager.writeStateLocked();
                }
                return true;
            }
        }
        return false;
    
private booleanshouldPersistPrintJob(android.print.PrintJobInfo printJob)

        return printJob.getState() >= PrintJobInfo.STATE_QUEUED;
    
public voidupdatePrintJobUserConfigurableOptionsNoPersistence(android.print.PrintJobInfo printJob)

        synchronized (mLock) {
            final int printJobCount = mPrintJobs.size();
            for (int i = 0; i < printJobCount; i++) {
                PrintJobInfo cachedPrintJob = mPrintJobs.get(i);
                if (cachedPrintJob.getId().equals(printJob.getId())) {
                    cachedPrintJob.setPrinterId(printJob.getPrinterId());
                    cachedPrintJob.setPrinterName(printJob.getPrinterName());
                    cachedPrintJob.setCopies(printJob.getCopies());
                    cachedPrintJob.setDocumentInfo(printJob.getDocumentInfo());
                    cachedPrintJob.setPages(printJob.getPages());
                    cachedPrintJob.setAttributes(printJob.getAttributes());
                    cachedPrintJob.setAdvancedOptions(printJob.getAdvancedOptions());
                    return;
                }
            }
            throw new IllegalArgumentException("No print job with id:" + printJob.getId());
        }
    
public voidwritePrintJobData(android.os.ParcelFileDescriptor fd, android.print.PrintJobId printJobId)

        final PrintJobInfo printJob;
        synchronized (mLock) {
            printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
        }
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                FileInputStream in = null;
                FileOutputStream out = null;
                try {
                    if (printJob != null) {
                        File file = generateFileForPrintJob(PrintSpoolerService.this, printJobId);
                        in = new FileInputStream(file);
                        out = new FileOutputStream(fd.getFileDescriptor());
                    }
                    final byte[] buffer = new byte[8192];
                    while (true) {
                        final int readByteCount = in.read(buffer);
                        if (readByteCount < 0) {
                            return null;
                        }
                        out.write(buffer, 0, readByteCount);
                    }
                } catch (FileNotFoundException fnfe) {
                    Log.e(LOG_TAG, "Error writing print job data!", fnfe);
                } catch (IOException ioe) {
                    Log.e(LOG_TAG, "Error writing print job data!", ioe);
                } finally {
                    IoUtils.closeQuietly(in);
                    IoUtils.closeQuietly(out);
                    IoUtils.closeQuietly(fd);
                }
                Log.i(LOG_TAG, "[END WRITE]");
                return null;
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);