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

Commit 8e9e8e5c authored by Aaron Massey's avatar Aaron Massey
Browse files

stats: Add PrinterDiscovery event

We want to log when new printers are added, what service discovered
them, and what their capabilities are.

Log the above information when adding new printers to the
PrintActivity.

Bug: 390478410
Test: atest PrintSpoolerRobolectricTests
Test: m PrintSpooler
Test: validate logged events via adb and webui
Flag: com.android.printspooler.flags.printing_telemetry
Change-Id: Ib5c47402d8d553b35737a74928808f35645a3c2f
parent ebf1ad71
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import java.util.Map;
 */
public final class PrintAttributes implements Parcelable {
    /** @hide */
    // LINT.IfChange
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = { "COLOR_MODE_" }, value = {
            COLOR_MODE_MONOCHROME,
@@ -62,9 +63,12 @@ public final class PrintAttributes implements Parcelable {

    private static final int VALID_COLOR_MODES =
            COLOR_MODE_MONOCHROME | COLOR_MODE_COLOR;
    // Update BuiltInPrintService stats logger too.
    // LINT.ThenChange(/packages/PrintSpooler/src/com/android/printspooler/stats/StatsAsyncLogger.kt)

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    // LINT.IfChange
    @IntDef(flag = true, prefix = { "DUPLEX_MODE_" }, value = {
            DUPLEX_MODE_NONE,
            DUPLEX_MODE_LONG_EDGE,
@@ -81,6 +85,8 @@ public final class PrintAttributes implements Parcelable {

    private static final int VALID_DUPLEX_MODES =
            DUPLEX_MODE_NONE | DUPLEX_MODE_LONG_EDGE | DUPLEX_MODE_SHORT_EDGE;
    // Update BuiltInPrintService stats logger too.
    // LINT.ThenChange(/packages/PrintSpooler/src/com/android/printspooler/stats/StatsAsyncLogger.kt)

    private @Nullable MediaSize mMediaSize;
    private @Nullable Resolution mResolution;
@@ -462,6 +468,7 @@ public final class PrintAttributes implements Parcelable {
        private static final Map<String, MediaSize> sIdToMediaSizeMap =
                new ArrayMap<>();

        // LINT.IfChange
        /**
         * Unknown media size in portrait mode.
         * <p>
@@ -840,6 +847,8 @@ public final class PrintAttributes implements Parcelable {
        public static final @NonNull MediaSize JPN_OE_PHOTO_L =
                new MediaSize("JPN_OE_PHOTO_L", "android",
                        R.string.mediasize_japanese_l, 3500, 5000);
        // Update BuiltInPrintService stats logger too.
        // LINT.ThenChange(/packages/PrintSpooler/src/com/android/printspooler/stats/StatsAsyncLogger.kt)

        private final @NonNull String mId;
        /**@hide */
+594 −0

File changed.

Preview size limit exceeded, changes collapsed.

+19 −0
Original line number Diff line number Diff line
@@ -49,4 +49,23 @@ open class StatsLogWrapper {
            printServiceCount,
        )
    }

    open fun internalPrinterDiscovery(
        @UserIdInt printServiceId: Int,
        supportedColors: Set<StatsAsyncLogger.InternalColorModePrinterDiscoveryEvent>,
        supportedSizes: Set<StatsAsyncLogger.InternalMediaSizePrinterDiscoveryEvent>,
        supportedDuplexModes: Set<StatsAsyncLogger.InternalDuplexModePrinterDiscoveryEvent>,
    ) {
        val colorBits = supportedColors.map { it.rawValue }.toIntArray()
        val mediaSizes = supportedSizes.map { it.rawValue }.toIntArray()
        val duplexModes = supportedDuplexModes.map { it.rawValue }.toIntArray()

        PrintSpoolerStatsLog.write(
            PrintSpoolerStatsLog.FRAMEWORK_PRINTER_DISCOVERY,
            printServiceId,
            colorBits,
            mediaSizes,
            duplexModes,
        )
    }
}
+25 −0
Original line number Diff line number Diff line
@@ -2663,6 +2663,31 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
                }
            }

            if (Flags.printingTelemetry()) {
                for (final PrinterInfo printer : newPrintersMap.values()) {
                    final PrinterCapabilitiesInfo caps = printer.getCapabilities();
                    int colorModesMask = 0;
                    int duplexModesMask = 0;
                    Set<MediaSize> mediaSizes = new HashSet<>();
                    if (caps != null) {
                        colorModesMask = caps.getColorModes();
                        duplexModesMask = caps.getDuplexModes();
                        mediaSizes = new HashSet<>(caps.getMediaSizes());
                    }
                    final String serviceName = printer.getId().getServiceName().getPackageName();
                    try {
                        final int serviceUId =
                                getPackageManager().getApplicationInfo(serviceName, 0).uid;
                        StatsAsyncLogger.INSTANCE
                                .PrinterDiscovery(serviceUId,
                                                  colorModesMask,
                                                  duplexModesMask,
                                                  mediaSizes);
                    } catch (NameNotFoundException e) {
                        Log.e(LOG_TAG, "Failed to get uid for service");
                    }
                }
            }
            // Add the rest of the new printers, i.e. what is left.
            addPrinters(newPrinterHolders, newPrintersMap.values());

+89 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.printspooler.stats

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

    @Test
    fun printerDiscoverySuccessfullyLoggedTest() {
        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 or PrintAttributes.COLOR_MODE_MONOCHROME)
        val supportedMediaSizesFoo =
            setOf<PrintAttributes.MediaSize>(
                PrintAttributes.MediaSize.NA_LETTER,
                PrintAttributes.MediaSize.JPN_HAGAKI,
            )
        val duplexModeMaskFoo =
            (PrintAttributes.DUPLEX_MODE_LONG_EDGE or PrintAttributes.DUPLEX_MODE_SHORT_EDGE)

        assertThat(
                StatsAsyncLogger.PrinterDiscovery(
                    printServiceFoo,
                    colorsMaskFoo,
                    duplexModeMaskFoo,
                    supportedMediaSizesFoo,
                )
            )
            .isTrue()

        // "bar" printer service: Generally arbitrary arguments focusing more on empty/default
        // values.
        val printServiceBar = 1337
        assertThat(StatsAsyncLogger.PrinterDiscovery(printServiceBar, 0, 0, setOf())).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)
            .internalPrinterDiscovery(
                printServiceFoo,
                setOf(
                    StatsAsyncLogger.InternalColorModePrinterDiscoveryEvent.COLOR,
                    StatsAsyncLogger.InternalColorModePrinterDiscoveryEvent.MONOCHROME,
                ),
                setOf(
                    StatsAsyncLogger.InternalMediaSizePrinterDiscoveryEvent.NA_LETTER,
                    StatsAsyncLogger.InternalMediaSizePrinterDiscoveryEvent.JPN_HAGAKI,
                ),
                setOf(
                    StatsAsyncLogger.InternalDuplexModePrinterDiscoveryEvent.LONG_EDGE,
                    StatsAsyncLogger.InternalDuplexModePrinterDiscoveryEvent.SHORT_EDGE,
                ),
            )
        logWrapperInOrder
            .verify(mStatsLogWrapper)
            .internalPrinterDiscovery(printServiceBar, setOf(), setOf(), setOf())
        logWrapperInOrder.verifyNoMoreInteractions()

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

    @Test
    fun mainPrintUiLaunchedSuccessfullyLoggedTest() {
        val logWrapperInOrder = inOrder(mStatsLogWrapper)
@@ -155,6 +240,7 @@ open class StatsAsyncLoggerTest {
        // Arbitrary Arguments
        assertThat(StatsAsyncLogger.AdvancedOptionsUiLaunched(42)).isFalse()
        assertThat(StatsAsyncLogger.MainPrintUiLaunched(setOf(1, 2, 3), 42)).isFalse()
        assertThat(StatsAsyncLogger.PrinterDiscovery(1337, 0, 0, setOf())).isFalse()
        verifyNoInteractions(mHandler)
    }

@@ -168,7 +254,8 @@ open class StatsAsyncLoggerTest {
        // Arbitrary Arguments
        assertThat(StatsAsyncLogger.AdvancedOptionsUiLaunched(42)).isFalse()
        assertThat(StatsAsyncLogger.MainPrintUiLaunched(setOf(1, 2, 3), 42)).isFalse()
        verify(mSemaphore, times(2)).release()
        assertThat(StatsAsyncLogger.PrinterDiscovery(1337, 0, 0, setOf())).isFalse()
        verify(mSemaphore, times(3)).release()
    }

    @Test
@@ -201,6 +288,7 @@ open class StatsAsyncLoggerTest {
        // Arbitrary Arguments
        assertThat(StatsAsyncLogger.AdvancedOptionsUiLaunched(42)).isFalse()
        assertThat(StatsAsyncLogger.MainPrintUiLaunched(setOf(1, 2, 3), 42)).isFalse()
        assertThat(StatsAsyncLogger.PrinterDiscovery(1337, 0, 0, setOf())).isFalse()
        verifyNoInteractions(mHandler)
        verifyNoInteractions(mSemaphore)
        verifyNoInteractions(mStatsLogWrapper)