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

Commit 33d6b13d authored by Aaron Massey's avatar Aaron Massey Committed by Android (Google) Code Review
Browse files

Merge "stats: Add PrintJob event" into main

parents fbb9bf7c 36f65aa0
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ import java.lang.annotation.RetentionPolicy;
 */
public final class PrintDocumentInfo implements Parcelable {

    // LINT.IfChange
    /**
     * Constant for unknown page count.
     */
@@ -119,6 +120,8 @@ public final class PrintDocumentInfo implements Parcelable {
     * </p>
     */
    public static final int CONTENT_TYPE_PHOTO = 1;
    // Update BuiltInPrintService stats logger too.
    // LINT.ThenChange(/packages/PrintSpooler/src/com/android/printspooler/stats/StatsAsyncLogger.kt)

    private @NonNull String mName;
    private @IntRange(from = -1) int mPageCount;
+54 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.Binder;
@@ -554,6 +555,58 @@ public final class PrintSpoolerService extends Service {
        mNotificationController.onUpdateNotifications(mPrintJobs);
    }

    // Stats Logging
    private void logPrintJobFinalState(PrinterId printerId, PrintJobInfo printJob) {
        if (!Flags.printingTelemetry()) {
            return;
        }
        final ComponentName service = (printerId == null) ? null : printerId.getServiceName();
        if (service == null) {
            // We don't know what to do without an identifiable service.
            Log.e(LOG_TAG, "Failed to get service ComponentName");
            return;
        }
        int serviceUId = 0;
        try {
            serviceUId =
                getPackageManager().getApplicationInfo(service.getPackageName(), 0).uid;
        } catch (NameNotFoundException e) {
            Log.e(LOG_TAG, String.format("Failed to get uid for service=%s",
                                         service.flattenToString()), e);
            // We don't know what to do without an identifiable service.
            return;
        }
        final boolean savedPdf = service.getPackageName().startsWith(this.getPackageName());
        final int state = printJob.getState();

        // The following values are all optional.

        final PrintAttributes attributes = printJob.getAttributes();
        final PrintAttributes.MediaSize size = (attributes == null)
                ? null : attributes.getMediaSize();
        final PrintAttributes.Resolution resolution = (attributes == null)
                ? null : attributes.getResolution();
        final int colorMode = (attributes == null) ? 0 : attributes.getColorMode();
        final int duplexMode = (attributes == null) ? 0 : attributes.getDuplexMode();

        final PrintDocumentInfo docInfo = printJob.getDocumentInfo();
        final int pageCount = (docInfo == null)
                ? PrintDocumentInfo.PAGE_COUNT_UNKNOWN : docInfo.getPageCount();
        final int docType = (docInfo == null)
                ? PrintDocumentInfo.CONTENT_TYPE_UNKNOWN : docInfo.getContentType();

        StatsAsyncLogger.INSTANCE.PrintJob(serviceUId,
                                           state,
                                           colorMode,
                                           size,
                                           resolution,
                                           duplexMode,
                                           docType,
                                           savedPdf,
                                           pageCount);

    }

    public boolean setPrintJobState(PrintJobId printJobId, int state, String error) {
        boolean success = false;

@@ -592,6 +645,7 @@ public final class PrintSpoolerService extends Service {
                    case PrintJobInfo.STATE_FAILED: {
                        PrinterId printerId = printJob.getPrinterId();
                        if (printerId != null) {
                            logPrintJobFinalState(printerId, printJob);
                            ComponentName service = printerId.getServiceName();
                            if (!hasActivePrintJobsForServiceLocked(service)) {
                                sendOnAllPrintJobsForServiceHandled(service);
+592 −0

File changed.

Preview size limit exceeded, changes collapsed.

+29 −0
Original line number Diff line number Diff line
@@ -68,4 +68,33 @@ open class StatsLogWrapper {
            duplexModes,
        )
    }

    open fun internalPrintJob(
        @UserIdInt printServiceId: Int,
        finalState: StatsAsyncLogger.InternalFinalStatePrintJobEvent,
        colorMode: StatsAsyncLogger.InternalColorModePrintJobEvent,
        duplexMode: StatsAsyncLogger.InternalDuplexModePrintJobEvent,
        mediaSize: StatsAsyncLogger.InternalMediaSizePrintJobEvent,
        docType: StatsAsyncLogger.InternalDocumentTypePrintJobEvent,
        orientation: StatsAsyncLogger.InternalOrientationPrintJobEvent,
        horizontalDpi: Int,
        verticalDpi: Int,
        savedPdf: Boolean,
        pageCount: Int,
    ) {
        PrintSpoolerStatsLog.write(
            PrintSpoolerStatsLog.FRAMEWORK_PRINT_JOB,
            finalState.rawValue,
            colorMode.rawValue,
            printServiceId,
            mediaSize.rawValue,
            horizontalDpi,
            verticalDpi,
            orientation.rawValue,
            duplexMode.rawValue,
            docType.rawValue,
            savedPdf,
            pageCount,
        )
    }
}
+141 −1
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.printspooler.stats

import android.os.Handler
import android.print.PrintAttributes
import android.print.PrintDocumentInfo
import android.print.PrintJobInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Semaphore
@@ -62,6 +64,116 @@ open class StatsAsyncLoggerTest {
        StatsAsyncLogger.stopLogging()
    }

    @Test
    fun printJobSuccessfullyLoggedTest() {
        val logWrapperInOrder = inOrder(mStatsLogWrapper)
        val handlerInOrder = inOrder(mHandler)
        val semaphoreInOrder = inOrder(mSemaphore)
        val timeCaptor = argumentCaptor<Long>()
        val runnableCaptor = argumentCaptor<Runnable>()

        StatsAsyncLogger.startLogging()
        StatsAsyncLogger.testSetSemaphore(mSemaphore)
        StatsAsyncLogger.testSetHandler(mHandler)
        StatsAsyncLogger.testSetStatsLogWrapper(mStatsLogWrapper)

        // "foo" printer service: Generally arbitrary arguments focusing more on creating non-empty
        // lists.
        val printServiceFoo = 42
        val colorsMaskFoo = PrintAttributes.COLOR_MODE_COLOR
        val sizeFoo = PrintAttributes.MediaSize.NA_LETTER
        val duplexModeMaskFoo = PrintAttributes.DUPLEX_MODE_LONG_EDGE
        val resolutionFoo = PrintAttributes.Resolution("hello", "resolution", 123, 321)
        val docTypeFoo = PrintDocumentInfo.CONTENT_TYPE_DOCUMENT
        val savedPdfFoo = true
        val pageCount = 52
        val finalState = PrintJobInfo.STATE_COMPLETED
        assertThat(
                StatsAsyncLogger.PrintJob(
                    printServiceFoo,
                    finalState,
                    colorsMaskFoo,
                    sizeFoo,
                    resolutionFoo,
                    duplexModeMaskFoo,
                    docTypeFoo,
                    savedPdfFoo,
                    pageCount,
                )
            )
            .isTrue()

        // "bar" printer service: Generally arbitrary arguments focusing more on empty/default
        // values.
        val printServiceBar = 1337
        assertThat(
                StatsAsyncLogger.PrintJob(
                    printServiceBar,
                    PrintJobInfo.STATE_FAILED,
                    0,
                    null,
                    null,
                    0,
                    PrintDocumentInfo.CONTENT_TYPE_UNKNOWN,
                    false,
                    PrintDocumentInfo.PAGE_COUNT_UNKNOWN,
                )
            )
            .isTrue()

        handlerInOrder
            .verify(mHandler, times(2))
            .postAtTime(runnableCaptor.capture(), timeCaptor.capture())
        handlerInOrder.verifyNoMoreInteractions()

        // Validate delay args
        val firstTime = timeCaptor.firstValue
        val secondTime = timeCaptor.secondValue
        assertThat(secondTime - firstTime)
            .isAtLeast(StatsAsyncLogger.EVENT_REPORTED_MIN_INTERVAL.inWholeMilliseconds)
        assertThat(secondTime - firstTime)
            .isAtMost(2 * StatsAsyncLogger.EVENT_REPORTED_MIN_INTERVAL.inWholeMilliseconds)

        // Validate Runnable logic
        runnableCaptor.firstValue.run()
        runnableCaptor.secondValue.run()
        logWrapperInOrder
            .verify(mStatsLogWrapper)
            .internalPrintJob(
                printServiceFoo,
                StatsAsyncLogger.InternalFinalStatePrintJobEvent.COMPLETED,
                StatsAsyncLogger.InternalColorModePrintJobEvent.COLOR,
                StatsAsyncLogger.InternalDuplexModePrintJobEvent.LONG_EDGE,
                StatsAsyncLogger.InternalMediaSizePrintJobEvent.NA_LETTER,
                StatsAsyncLogger.InternalDocumentTypePrintJobEvent.DOCUMENT,
                StatsAsyncLogger.InternalOrientationPrintJobEvent.PORTRAIT,
                resolutionFoo.getHorizontalDpi(),
                resolutionFoo.getVerticalDpi(),
                savedPdfFoo,
                pageCount,
            )
        logWrapperInOrder
            .verify(mStatsLogWrapper)
            .internalPrintJob(
                printServiceBar,
                StatsAsyncLogger.InternalFinalStatePrintJobEvent.FAILED,
                StatsAsyncLogger.InternalColorModePrintJobEvent.UNSPECIFIED,
                StatsAsyncLogger.InternalDuplexModePrintJobEvent.UNSPECIFIED,
                StatsAsyncLogger.InternalMediaSizePrintJobEvent.UNSPECIFIED,
                StatsAsyncLogger.InternalDocumentTypePrintJobEvent.UNSPECIFIED,
                StatsAsyncLogger.InternalOrientationPrintJobEvent.UNSPECIFIED,
                0,
                0,
                false,
                PrintDocumentInfo.PAGE_COUNT_UNKNOWN,
            )
        logWrapperInOrder.verifyNoMoreInteractions()

        // Validate Semaphore logic
        semaphoreInOrder.verify(mSemaphore, times(2)).tryAcquire()
        semaphoreInOrder.verify(mSemaphore, times(2)).release()
    }

    @Test
    fun printerDiscoverySuccessfullyLoggedTest() {
        val logWrapperInOrder = inOrder(mStatsLogWrapper)
@@ -255,7 +367,21 @@ open class StatsAsyncLoggerTest {
        assertThat(StatsAsyncLogger.AdvancedOptionsUiLaunched(42)).isFalse()
        assertThat(StatsAsyncLogger.MainPrintUiLaunched(setOf(1, 2, 3), 42)).isFalse()
        assertThat(StatsAsyncLogger.PrinterDiscovery(1337, 0, 0, setOf())).isFalse()
        verify(mSemaphore, times(3)).release()
        assertThat(
                StatsAsyncLogger.PrintJob(
                    42,
                    PrintJobInfo.STATE_FAILED,
                    0,
                    null,
                    null,
                    0,
                    PrintDocumentInfo.CONTENT_TYPE_UNKNOWN,
                    false,
                    PrintDocumentInfo.PAGE_COUNT_UNKNOWN,
                )
            )
            .isFalse()
        verify(mSemaphore, times(4)).release()
    }

    @Test
@@ -289,6 +415,20 @@ open class StatsAsyncLoggerTest {
        assertThat(StatsAsyncLogger.AdvancedOptionsUiLaunched(42)).isFalse()
        assertThat(StatsAsyncLogger.MainPrintUiLaunched(setOf(1, 2, 3), 42)).isFalse()
        assertThat(StatsAsyncLogger.PrinterDiscovery(1337, 0, 0, setOf())).isFalse()
        assertThat(
                StatsAsyncLogger.PrintJob(
                    42,
                    PrintJobInfo.STATE_FAILED,
                    0,
                    null,
                    null,
                    0,
                    PrintDocumentInfo.CONTENT_TYPE_UNKNOWN,
                    false,
                    PrintDocumentInfo.PAGE_COUNT_UNKNOWN,
                )
            )
            .isFalse()
        verifyNoInteractions(mHandler)
        verifyNoInteractions(mSemaphore)
        verifyNoInteractions(mStatsLogWrapper)