Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b84aa9fe authored by Svetoslav Ganov's avatar Svetoslav Ganov Committed by Android Git Automerger
Browse files

am b415525a: Merge "Print job files and print job records not always cleaned up." into klp-dev

* commit 'b415525a':
  Print job files and print job records not always cleaned up.
parents 98b96d2c b415525a
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import android.print.PrintJobInfo;
 */
oneway interface IPrintSpooler {
    void removeObsoletePrintJobs();
    void forgetPrintJobs(in List<PrintJobId> printJob);
    void getPrintJobInfos(IPrintSpoolerCallbacks callback, in ComponentName componentName,
            int state, int appId, int sequence);
    void getPrintJobInfo(in PrintJobId printJobId, IPrintSpoolerCallbacks callback,
+1 −1
Original line number Diff line number Diff line
@@ -29,5 +29,5 @@ oneway interface IPrintSpoolerClient {
    void onPrintJobQueued(in PrintJobInfo printJob);
    void onAllPrintJobsForServiceHandled(in ComponentName printService);
    void onAllPrintJobsHandled();
    void onPrintJobStateChanged(in PrintJobId printJobId, int appId);
    void onPrintJobStateChanged(in PrintJobInfo printJob);
}
+83 −44
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.print.PrintManager;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -59,10 +60,12 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

@@ -82,6 +85,8 @@ public final class PrintSpoolerService extends Service {

    private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;

    private static final String PRINT_JOB_FILE_PREFIX = "print_job_";

    private static final String PRINT_FILE_EXTENSION = "pdf";

    private static final Object sLock = new Object();
@@ -168,9 +173,9 @@ public final class PrintSpoolerService extends Service {
                        PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT
                        | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();

                Message message = mHandlerCaller.obtainMessageIIO(
                Message message = mHandlerCaller.obtainMessageO(
                        HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
                        printJob.getAppId(), 0, printJob.getId());
                        printJob);
                mHandlerCaller.executeOrSendMessage(message);

                message = mHandlerCaller.obtainMessageOO(
@@ -179,9 +184,6 @@ public final class PrintSpoolerService extends Service {
                mHandlerCaller.executeOrSendMessage(message);

                printJob.setCreationTime(System.currentTimeMillis());
                synchronized (mLock) {
                    mPersistanceManager.writeStateLocked();
                }
            }

            @Override
@@ -225,12 +227,40 @@ public final class PrintSpoolerService extends Service {
            }

            @Override
            public void forgetPrintJobs(List<PrintJobId> printJobIds) {
                PrintSpoolerService.this.forgetPrintJobs(printJobIds);
            protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
                PrintSpoolerService.this.dump(fd, writer, args);
            }
        };
    }

    @Override
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        synchronized (mLock) {
            String prefix = 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();
                    }
                }
            }
        }
    }

    private void sendOnPrintJobQueued(PrintJobInfo printJob) {
        Message message = mHandlerCaller.obtainMessageO(
                HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
@@ -324,10 +354,9 @@ public final class PrintSpoolerService extends Service {

                case MSG_ON_PRINT_JOB_STATE_CHANGED: {
                    if (mClient != null) {
                        PrintJobId printJobId = (PrintJobId) message.obj;
                        final int appId = message.arg1;
                        PrintJobInfo printJob = (PrintJobInfo) message.obj;
                        try {
                            mClient.onPrintJobStateChanged(printJobId, appId);
                            mClient.onPrintJobStateChanged(printJob);
                        } catch (RemoteException re) {
                            Slog.e(LOG_TAG, "Error notify for print job state change.", re);
                        }
@@ -391,17 +420,46 @@ public final class PrintSpoolerService extends Service {
    public void createPrintJob(PrintJobInfo printJob) {
        synchronized (mLock) {
            addPrintJobLocked(printJob);
            setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);
        }
    }

    private void handleReadPrintJobsLocked() {
        // 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(0,
                            PRINT_JOB_FILE_PREFIX.length());
                    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());
            }

            // Update the notification.
            mNotificationController.onPrintJobStateChanged(printJob);

            switch (printJob.getState()) {
                case PrintJobInfo.STATE_QUEUED:
                case PrintJobInfo.STATE_STARTED:
@@ -415,6 +473,15 @@ public final class PrintSpoolerService extends Service {
                } break;
            }
        }

        // 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 void checkAllPrintJobsHandled() {
@@ -465,7 +532,7 @@ public final class PrintSpoolerService extends Service {
    }

    public File generateFileForPrintJob(PrintJobId printJobId) {
        return new File(getFilesDir(), "print_job_"
        return new File(getFilesDir(), PRINT_JOB_FILE_PREFIX
                + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION);
    }

@@ -476,31 +543,6 @@ public final class PrintSpoolerService extends Service {
        }
    }

    private void forgetPrintJobs(List<PrintJobId> printJobIds) {
        synchronized (mLock) {
            boolean printJobsRemoved = false;
            final int removedPrintJobCount = printJobIds.size();
            for (int i = 0; i < removedPrintJobCount; i++) {
                PrintJobId removedPrintJobId = printJobIds.get(i);
                final int printJobCount = mPrintJobs.size();
                for (int j = printJobCount - 1; j >= 0; j--) {
                    PrintJobInfo printJob = mPrintJobs.get(j);
                    if (removedPrintJobId.equals(printJob.getId())) {
                        mPrintJobs.remove(j);
                        printJobsRemoved = true;
                        if (DEBUG_PRINT_JOB_LIFECYCLE) {
                            Slog.i(LOG_TAG, "[FORGOT] " + printJob.getId().flattenToString());
                        }
                        removePrintJobFileLocked(printJob.getId());
                    }
                }
            }
            if (printJobsRemoved) {
                mPersistanceManager.writeStateLocked();
            }
        }
    }

    private void removeObsoletePrintJobs() {
        synchronized (mLock) {
            final int printJobCount = mPrintJobs.size();
@@ -523,7 +565,7 @@ public final class PrintSpoolerService extends Service {
        if (file.exists()) {
            file.delete();
            if (DEBUG_PRINT_JOB_LIFECYCLE) {
                Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId.flattenToString());
                Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId);
            }
        }
    }
@@ -552,10 +594,7 @@ public final class PrintSpoolerService extends Service {
                switch (state) {
                    case PrintJobInfo.STATE_COMPLETED:
                    case PrintJobInfo.STATE_CANCELED:
                        // Just remove the file but keep the print job info since
                        // the app that created it may be holding onto the PrintJob
                        // instance and query it for its most recent state. We will
                        // remove the info for this job when told so by the system.
                        mPrintJobs.remove(printJob);
                        removePrintJobFileLocked(printJob.getId());
                        // $fall-through$

@@ -582,9 +621,9 @@ public final class PrintSpoolerService extends Service {
                    notifyOnAllPrintJobsHandled();
                }

                Message message = mHandlerCaller.obtainMessageIIO(
                Message message = mHandlerCaller.obtainMessageO(
                        HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
                        printJob.getAppId(), 0, printJob.getId());
                        printJob);
                mHandlerCaller.executeOrSendMessage(message);
            }
        }
+14 −42
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@ import android.print.IPrintSpoolerCallbacks;
import android.print.IPrintSpoolerClient;
import android.print.PrintJobId;
import android.print.PrintJobInfo;
import android.print.PrintManager;
import android.util.Slog;
import android.util.TimedRemoteCaller;

@@ -91,7 +90,7 @@ final class RemotePrintSpooler {
    public static interface PrintSpoolerCallbacks {
        public void onPrintJobQueued(PrintJobInfo printJob);
        public void onAllPrintJobsForServiceHandled(ComponentName printService);
        public void onPrintJobStateChanged(PrintJobId printJobId, int appId);
        public void onPrintJobStateChanged(PrintJobInfo printJob);
    }

    public RemotePrintSpooler(Context context, int userId,
@@ -280,30 +279,6 @@ final class RemotePrintSpooler {
        }
    }

    public final void forgetPrintJobs(List<PrintJobId> printJobIds) {
        throwIfCalledOnMainThread();
        synchronized (mLock) {
            throwIfDestroyedLocked();
            mCanUnbind = false;
        }
        try {
            getRemoteInstanceLazy().forgetPrintJobs(printJobIds);
        } catch (RemoteException re) {
            Slog.e(LOG_TAG, "Error forgeting print jobs", re);
        } catch (TimeoutException te) {
            Slog.e(LOG_TAG, "Error forgeting print jobs", te);
        } finally {
            if (DEBUG) {
                Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
                        + "] forgetPrintJobs()");
            }
            synchronized (mLock) {
                mCanUnbind = true;
                mLock.notifyAll();
            }
        }
    }

    public final void destroy() {
        throwIfCalledOnMainThread();
        if (DEBUG) {
@@ -323,18 +298,15 @@ final class RemotePrintSpooler {
                    .append(String.valueOf(mDestroyed)).println();
            pw.append(prefix).append("bound=")
                    .append((mRemoteInstance != null) ? "true" : "false").println();
            pw.append(prefix).append("print jobs:").println();
            if (mRemoteInstance != null) {
                List<PrintJobInfo> printJobs = getPrintJobInfos(null,
                        PrintJobInfo.STATE_ANY, PrintManager.APP_ID_ANY);
                if (printJobs != null) {
                    final int printJobCount = printJobs.size();
                    for (int i = 0; i < printJobCount; i++) {
                        PrintJobInfo printJob = printJobs.get(i);
                        pw.append(prefix).append(prefix).append(printJob.toString());
                        pw.println();
                    }
                }

            pw.flush();

            try {
                getRemoteInstanceLazy().asBinder().dump(fd, new String[]{prefix});
            } catch (TimeoutException te) {
                /* ignore */
            } catch (RemoteException re) {
                /* ignore */
            }
        }
    }
@@ -346,8 +318,8 @@ final class RemotePrintSpooler {
        }
    }

    private void onPrintJobStateChanged(PrintJobId printJobId, int appId) {
        mCallbacks.onPrintJobStateChanged(printJobId, appId);
    private void onPrintJobStateChanged(PrintJobInfo printJob) {
        mCallbacks.onPrintJobStateChanged(printJob);
    }

    private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException {
@@ -625,12 +597,12 @@ final class RemotePrintSpooler {
        }

        @Override
        public void onPrintJobStateChanged(PrintJobId printJobId, int appId) {
        public void onPrintJobStateChanged(PrintJobInfo printJob) {
            RemotePrintSpooler spooler = mWeakSpooler.get();
            if (spooler != null) {
                final long identity = Binder.clearCallingIdentity();
                try {
                    spooler.onPrintJobStateChanged(printJobId, appId);
                    spooler.onPrintJobStateChanged(printJob);
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
+139 −41
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserManager;
import android.print.IPrintClient;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintJobStateChangeListener;
@@ -52,6 +51,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.R;
import com.android.internal.os.BackgroundThread;
@@ -62,6 +62,7 @@ import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -93,8 +94,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
    private final Set<ComponentName> mEnabledServices =
            new ArraySet<ComponentName>();

    private final CreatedPrintJobTracker mCreatedPrintJobTracker =
            new CreatedPrintJobTracker();
    private final PrintJobForAppCache mPrintJobForAppCache =
            new PrintJobForAppCache();

    private final Object mLock;

@@ -155,23 +156,22 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
    public PrintJobInfo print(String printJobName, final IPrintClient client,
            final IPrintDocumentAdapter documentAdapter, PrintAttributes attributes,
            int appId) {
        PrintJobId printJobId = new PrintJobId();

        // Track this job so we can forget it when the creator dies.
        if (!mCreatedPrintJobTracker.onPrintJobCreatedLocked(client.asBinder(), printJobId)) {
            // Not adding a print job means the client is dead - done.
            return null;
        }

        // Create print job place holder.
        final PrintJobInfo printJob = new PrintJobInfo();
        printJob.setId(printJobId);
        printJob.setId(new PrintJobId());
        printJob.setAppId(appId);
        printJob.setLabel(printJobName);
        printJob.setAttributes(attributes);
        printJob.setState(PrintJobInfo.STATE_CREATED);
        printJob.setCopies(1);

        // Track this job so we can forget it when the creator dies.
        if (!mPrintJobForAppCache.onPrintJobCreated(client.asBinder(), appId,
                printJob)) {
            // Not adding a print job means the client is dead - done.
            return null;
        }

        // Spin the spooler to add the job and show the config UI.
        new AsyncTask<Void, Void, Void>() {
            @Override
@@ -185,10 +185,40 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
    }

    public List<PrintJobInfo> getPrintJobInfos(int appId) {
        return mSpooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY, appId);
        List<PrintJobInfo> cachedPrintJobs = mPrintJobForAppCache.getPrintJobs(appId);
        // Note that the print spooler is not storing print jobs that
        // are in a terminal state as it is non-trivial to properly update
        // the spooler state for when to forget print jobs in terminal state.
        // Therefore, we fuse the cached print jobs for running apps (some
        // jobs are in a terminal state) with the ones that the print
        // spooler knows about (some jobs are being processed).
        ArrayMap<PrintJobId, PrintJobInfo> result =
                new ArrayMap<PrintJobId, PrintJobInfo>();

        // Add the cached print jobs for running apps.
        final int cachedPrintJobCount = cachedPrintJobs.size();
        for (int i = 0; i < cachedPrintJobCount; i++) {
            PrintJobInfo cachedPrintJob = cachedPrintJobs.get(i);
            result.put(cachedPrintJob.getId(), cachedPrintJob);
        }

        // Add everything else the spooler knows about.
        List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(null,
                PrintJobInfo.STATE_ANY, appId);
        final int printJobCount = printJobs.size();
        for (int i = 0; i < printJobCount; i++) {
            PrintJobInfo printJob = printJobs.get(i);
            result.put(printJob.getId(), printJob);
        }

        return new ArrayList<PrintJobInfo>(result.values());
    }

    public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
        PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId);
        if (printJob != null) {
            return printJob;
        }
        return mSpooler.getPrintJobInfo(printJobId, appId);
    }

@@ -398,9 +428,10 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
    }

    @Override
    public void onPrintJobStateChanged(PrintJobId printJobId, int appId) {
    public void onPrintJobStateChanged(PrintJobInfo printJob) {
        mPrintJobForAppCache.onPrintJobStateChanged(printJob);
        mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED,
                appId, 0, printJobId).sendToTarget();
                printJob.getAppId(), 0, printJob.getId()).sendToTarget();
    }

    @Override
@@ -525,6 +556,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
            pw.println();
        }

        pw.append(prefix).append(tab).append("cached print jobs:").println();
        mPrintJobForAppCache.dump(pw, prefix + tab + tab);

        pw.append(prefix).append(tab).append("discovery mediator:").println();
        if (mPrinterDiscoverySession != null) {
            mPrinterDiscoverySession.dump(pw, prefix + tab + tab);
@@ -1424,34 +1458,19 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
        }
    }

    private final class CreatedPrintJobTracker {
        private final ArrayMap<IBinder, List<PrintJobId>> mCreatedPrintJobs =
                new ArrayMap<IBinder, List<PrintJobId>>();
    private final class PrintJobForAppCache {
        private final SparseArray<List<PrintJobInfo>> mPrintJobsForRunningApp =
                new SparseArray<List<PrintJobInfo>>();

        public boolean onPrintJobCreatedLocked(final IBinder creator, PrintJobId printJobId) {
        public boolean onPrintJobCreated(final IBinder creator, final int appId,
                PrintJobInfo printJob) {
            try {
                creator.linkToDeath(new DeathRecipient() {
                    @Override
                    public void binderDied() {
                        creator.unlinkToDeath(this, 0);
                        UserManager userManager = (UserManager) mContext.getSystemService(
                                Context.USER_SERVICE);
                        // If the death is a result of the user being removed, then
                        // do nothing since the spooler data for this user will be
                        // wiped and we cannot bind to the spooler at this point.
                        if (userManager.getUserInfo(mUserId) == null) {
                            return;
                        }
                        List<PrintJobId> printJobIds = null;
                        synchronized (mLock) {
                            printJobIds = mCreatedPrintJobs.remove(creator);
                            if (printJobIds == null) {
                                return;
                            }
                            printJobIds = new ArrayList<PrintJobId>(printJobIds);
                        }
                        if (printJobIds != null) {
                            mSpooler.forgetPrintJobs(printJobIds);
                            mPrintJobsForRunningApp.remove(appId);
                        }
                    }
                }, 0);
@@ -1460,14 +1479,93 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
                return false;
            }
            synchronized (mLock) {
                List<PrintJobId> printJobIds = mCreatedPrintJobs.get(creator);
                if (printJobIds == null) {
                    printJobIds = new ArrayList<PrintJobId>();
                    mCreatedPrintJobs.put(creator, printJobIds);
                List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId);
                if (printJobsForApp == null) {
                    printJobsForApp = new ArrayList<PrintJobInfo>();
                    mPrintJobsForRunningApp.put(appId, printJobsForApp);
                }
                printJobIds.add(printJobId);
                printJobsForApp.add(printJob);
            }
            return true;
        }

        public void onPrintJobStateChanged(PrintJobInfo printJob) {
            synchronized (mLock) {
                List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(
                        printJob.getAppId());
                if (printJobsForApp == null) {
                    return;
                }
                final int printJobCount = printJobsForApp.size();
                for (int i = 0; i < printJobCount; i++) {
                    PrintJobInfo oldPrintJob = printJobsForApp.get(i);
                    if (oldPrintJob.getId().equals(printJob.getId())) {
                        printJobsForApp.set(i, printJob);
                    }
                }
            }
        }

        public PrintJobInfo getPrintJob(PrintJobId printJobId, int appId) {
            synchronized (mLock) {
                List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId);
                if (printJobsForApp == null) {
                    return null;
                }
                final int printJobCount = printJobsForApp.size();
                for (int i = 0; i < printJobCount; i++) {
                    PrintJobInfo printJob = printJobsForApp.get(i);
                    if (printJob.getId().equals(printJobId)) {
                        return printJob;
                    }
                }
            }
            return null;
        }

        public List<PrintJobInfo> getPrintJobs(int appId) {
            synchronized (mLock) {
                List<PrintJobInfo> printJobs = null;
                if (appId == PrintManager.APP_ID_ANY) {
                    final int bucketCount = mPrintJobsForRunningApp.size();
                    for (int i = 0; i < bucketCount; i++) {
                        List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
                        if (printJobs == null) {
                            printJobs = new ArrayList<PrintJobInfo>();
                        }
                        printJobs.addAll(bucket);
                    }
                } else {
                    List<PrintJobInfo> bucket = mPrintJobsForRunningApp.get(appId);
                    if (bucket != null) {
                        if (printJobs == null) {
                            printJobs = new ArrayList<PrintJobInfo>();
                        }
                        printJobs.addAll(bucket);
                    }
                }
                if (printJobs != null) {
                    return printJobs;
                }
                return Collections.emptyList();
            }
        }

        public void dump(PrintWriter pw, String prefix) {
            synchronized (mLock) {
                String tab = "  ";
                final int bucketCount = mPrintJobsForRunningApp.size();
                for (int i = 0; i < bucketCount; i++) {
                    final int appId = mPrintJobsForRunningApp.keyAt(i);
                    pw.append(prefix).append("appId=" + appId).append(':').println();
                    List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
                    final int printJobCount = bucket.size();
                    for (int j = 0; j < printJobCount; j++) {
                        PrintJobInfo printJob = bucket.get(j);
                        pw.append(prefix).append(tab).append(printJob.toString()).println();
                    }
                }
            }
        }
    }
}