diff options
21 files changed, 1545 insertions, 63 deletions
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 3eb487461e5c..58f260cbe1cf 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -535,7 +535,10 @@ public final class PrintManager { return new PrinterDiscoverySession(mService, mContext, mUserId); } - private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub + /** + * @hide + */ + public static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub implements ActivityLifecycleCallbacks { private final Object mLock = new Object(); @@ -1061,7 +1064,10 @@ public final class PrintManager { } } - private static final class PrintJobStateChangeListenerWrapper extends + /** + * @hide + */ + public static final class PrintJobStateChangeListenerWrapper extends IPrintJobStateChangeListener.Stub { private final WeakReference<PrintJobStateChangeListener> mWeakListener; private final WeakReference<Handler> mWeakHandler; diff --git a/core/java/android/print/PrinterDiscoverySession.java b/core/java/android/print/PrinterDiscoverySession.java index abb441b79bc1..c587edde78f7 100644 --- a/core/java/android/print/PrinterDiscoverySession.java +++ b/core/java/android/print/PrinterDiscoverySession.java @@ -16,6 +16,8 @@ package android.print; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.Handler; @@ -72,7 +74,7 @@ public final class PrinterDiscoverySession { } } - public final void startPrinterDiscovery(List<PrinterId> priorityList) { + public final void startPrinterDiscovery(@Nullable List<PrinterId> priorityList) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring start printers discovery - session destroyed"); return; @@ -102,7 +104,7 @@ public final class PrinterDiscoverySession { } } - public final void startPrinterStateTracking(PrinterId printerId) { + public final void startPrinterStateTracking(@NonNull PrinterId printerId) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring start printer state tracking - session destroyed"); return; @@ -114,7 +116,7 @@ public final class PrinterDiscoverySession { } } - public final void stopPrinterStateTracking(PrinterId printerId) { + public final void stopPrinterStateTracking(@NonNull PrinterId printerId) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring stop printer state tracking - session destroyed"); return; @@ -285,7 +287,7 @@ public final class PrinterDiscoverySession { } } - private static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub { + public static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub { private final WeakReference<PrinterDiscoverySession> mWeakSession; diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index 310449222a0b..62d214e9e43c 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -16,6 +16,7 @@ package android.printservice; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Service; import android.content.ComponentName; @@ -346,7 +347,7 @@ public abstract class PrintService extends Service { * @param localId A locally unique id in the context of your print service. * @return Global printer id. */ - public final PrinterId generatePrinterId(String localId) { + public @NonNull final PrinterId generatePrinterId(String localId) { throwIfNotCalledOnMainThread(); localId = Preconditions.checkNotNull(localId, "localId cannot be null"); return new PrinterId(new ComponentName(getPackageName(), diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index 381e71f7f333..2f26e921d03b 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -42,6 +42,23 @@ public class Preconditions { * @return the string reference that was validated * @throws IllegalArgumentException if {@code string} is empty */ + public static @NonNull String checkStringNotEmpty(final String string) { + if (TextUtils.isEmpty(string)) { + throw new IllegalArgumentException(); + } + return string; + } + + /** + * Ensures that an string reference passed as a parameter to the calling + * method is not empty. + * + * @param string an string reference + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @return the string reference that was validated + * @throws IllegalArgumentException if {@code string} is empty + */ public static @NonNull String checkStringNotEmpty(final String string, final Object errorMessage) { if (TextUtils.isEmpty(string)) { @@ -301,8 +318,8 @@ public class Preconditions { * * @throws NullPointerException if the {@code value} or any of its elements were {@code null} */ - public static <T> Collection<T> checkCollectionElementsNotNull(final Collection<T> value, - final String valueName) { + public static @NonNull <C extends Collection<T>, T> C checkCollectionElementsNotNull( + final C value, final String valueName) { if (value == null) { throw new NullPointerException(valueName + " must not be null"); } diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index ee8921ef8228..eb055de01c63 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -32,7 +32,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ littlemock \ android-support-test \ mockito-target \ - espresso-core + espresso-core \ + ub-uiautomator LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy LOCAL_PACKAGE_NAME := FrameworksCoreTests diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index eb0075b6b0b7..bfa2b10fdf4f 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1295,6 +1295,26 @@ </intent-filter> </activity> + <activity android:name="android.print.PrintTestActivity"/> + + <service + android:name="android.print.mockservice.MockPrintService" + android:permission="android.permission.BIND_PRINT_SERVICE"> + <intent-filter> + <action android:name="android.printservice.PrintService" /> + </intent-filter> + <meta-data + android:name="android.printservice" + android:resource="@xml/printservice"> + </meta-data> + </service> + + <activity + android:name="android.print.mockservice.SettingsActivity" + android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY" + android:exported="true"> + </activity> + </application> <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" diff --git a/core/tests/coretests/res/xml/printservice.xml b/core/tests/coretests/res/xml/printservice.xml new file mode 100644 index 000000000000..abbebdae0d57 --- /dev/null +++ b/core/tests/coretests/res/xml/printservice.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Copyright (C) 2016 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. +--> + +<print-service xmlns:android="http://schemas.android.com/apk/res/android" + android:settingsActivity="android.print.mockservice.SettingsActivity"/> diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java new file mode 100644 index 000000000000..19ce44a90815 --- /dev/null +++ b/core/tests/coretests/src/android/print/BasePrintTest.java @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2016 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 android.print; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.app.Instrumentation; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.ParcelFileDescriptor; +import android.os.SystemClock; +import android.print.PrintAttributes; +import android.print.PrintDocumentAdapter; +import android.print.PrintManager; +import android.print.PrinterId; +import android.print.mockservice.PrintServiceCallbacks; +import android.print.mockservice.PrinterDiscoverySessionCallbacks; +import android.print.mockservice.StubbablePrinterDiscoverySession; +import android.printservice.CustomPrinterIconCallback; +import android.printservice.PrintJob; +import android.printservice.PrintService; +import android.test.InstrumentationTestCase; +import android.util.DisplayMetrics; +import android.util.LocaleList; + +import org.mockito.stubbing.Answer; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeoutException; + +/** + * This is the base class for print tests. + */ +public abstract class BasePrintTest extends InstrumentationTestCase { + + private static final long OPERATION_TIMEOUT = 30000; + private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler"; + private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success"; + private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s"; + private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable "; + private static final String COMMAND_PREFIX_DISABLE_IME = "ime disable "; + private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT + + private PrintTestActivity mActivity; + private android.print.PrintJob mPrintJob; + + private LocaleList mOldLocale; + + private CallCounter mStartCallCounter; + private CallCounter mStartSessionCallCounter; + + private String[] mEnabledImes; + + private String[] getEnabledImes() throws IOException { + List<String> imeList = new ArrayList<>(); + + ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() + .executeShellCommand(COMMAND_LIST_ENABLED_IME_COMPONENTS); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())))) { + + String line; + while ((line = reader.readLine()) != null) { + imeList.add(line); + } + } + + String[] imeArray = new String[imeList.size()]; + imeList.toArray(imeArray); + + return imeArray; + } + + private void disableImes() throws Exception { + mEnabledImes = getEnabledImes(); + for (String ime : mEnabledImes) { + String disableImeCommand = COMMAND_PREFIX_DISABLE_IME + ime; + runShellCommand(getInstrumentation(), disableImeCommand); + } + } + + private void enableImes() throws Exception { + for (String ime : mEnabledImes) { + String enableImeCommand = COMMAND_PREFIX_ENABLE_IME + ime; + runShellCommand(getInstrumentation(), enableImeCommand); + } + mEnabledImes = null; + } + + @Override + protected void runTest() throws Throwable { + // Do nothing if the device does not support printing. + if (supportsPrinting()) { + super.runTest(); + } + } + + @Override + public void setUp() throws Exception { + super.setUp(); + if (!supportsPrinting()) { + return; + } + + // Make sure we start with a clean slate. + clearPrintSpoolerData(); + disableImes(); + + // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2 + // Dexmaker is used by mockito. + System.setProperty("dexmaker.dexcache", getInstrumentation() + .getTargetContext().getCacheDir().getPath()); + + // Set to US locale. + Resources resources = getInstrumentation().getTargetContext().getResources(); + Configuration oldConfiguration = resources.getConfiguration(); + if (!oldConfiguration.getLocales().getPrimary().equals(Locale.US)) { + mOldLocale = oldConfiguration.getLocales(); + DisplayMetrics displayMetrics = resources.getDisplayMetrics(); + Configuration newConfiguration = new Configuration(oldConfiguration); + newConfiguration.setLocale(Locale.US); + resources.updateConfiguration(newConfiguration, displayMetrics); + } + + // Initialize the latches. + mStartCallCounter = new CallCounter(); + mStartSessionCallCounter = new CallCounter(); + + // Create the activity for the right locale. + createActivity(); + } + + @Override + public void tearDown() throws Exception { + if (!supportsPrinting()) { + return; + } + + // Done with the activity. + getActivity().finish(); + enableImes(); + + // Restore the locale if needed. + if (mOldLocale != null) { + Resources resources = getInstrumentation().getTargetContext().getResources(); + DisplayMetrics displayMetrics = resources.getDisplayMetrics(); + Configuration newConfiguration = new Configuration(resources.getConfiguration()); + newConfiguration.setLocales(mOldLocale); + mOldLocale = null; + resources.updateConfiguration(newConfiguration, displayMetrics); + } + + // Make sure the spooler is cleaned, this also un-approves all services + clearPrintSpoolerData(); + + super.tearDown(); + } + + protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter, + final PrintAttributes attributes) { + // Initiate printing as if coming from the app. + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + PrintManager printManager = (PrintManager) getActivity() + .getSystemService(Context.PRINT_SERVICE); + mPrintJob = printManager.print("Print job", adapter, attributes); + } + }); + + return mPrintJob; + } + + protected void onStartCalled() { + mStartCallCounter.call(); + } + + protected void onPrinterDiscoverySessionStartCalled() { + mStartSessionCallCounter.call(); + } + + protected void waitForPrinterDiscoverySessionStartCallbackCalled() { + waitForCallbackCallCount(mStartSessionCallCounter, 1, + "Did not get expected call to onStartPrinterDiscoverySession."); + } + + protected void waitForStartAdapterCallbackCalled() { + waitForCallbackCallCount(mStartCallCounter, 1, "Did not get expected call to start."); + } + + private void waitForCallbackCallCount(CallCounter counter, int count, String message) { + try { + counter.waitForCount(count, OPERATION_TIMEOUT); + } catch (TimeoutException te) { + fail(message); + } + } + + protected PrintTestActivity getActivity() { + return mActivity; + } + + protected void createActivity() { + mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(), + PrintTestActivity.class, null); + } + + public static String runShellCommand(Instrumentation instrumentation, String cmd) + throws IOException { + ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd); + byte[] buf = new byte[512]; + int bytesRead; + FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + StringBuffer stdout = new StringBuffer(); + while ((bytesRead = fis.read(buf)) != -1) { + stdout.append(new String(buf, 0, bytesRead)); + } + fis.close(); + return stdout.toString(); + } + + protected void clearPrintSpoolerData() throws Exception { + assertTrue("failed to clear print spooler data", + runShellCommand(getInstrumentation(), String.format( + "pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME)) + .contains(PM_CLEAR_SUCCESS_OUTPUT)); + } + + @SuppressWarnings("unchecked") + protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks( + Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery, + Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking, + Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking, + Answer<Void> onDestroy) { + PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class); + + doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class)); + when(callbacks.getSession()).thenCallRealMethod(); + + if (onStartPrinterDiscovery != null) { + doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery( + any(List.class)); + } + if (onStopPrinterDiscovery != null) { + doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery(); + } + if (onValidatePrinters != null) { + doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters( + any(List.class)); + } + if (onStartPrinterStateTracking != null) { + doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking( + any(PrinterId.class)); + } + if (onRequestCustomPrinterIcon != null) { + doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon( + any(PrinterId.class), any(CustomPrinterIconCallback.class)); + } + if (onStopPrinterStateTracking != null) { + doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking( + any(PrinterId.class)); + } + if (onDestroy != null) { + doAnswer(onDestroy).when(callbacks).onDestroy(); + } + + return callbacks; + } + + protected PrintServiceCallbacks createMockPrintServiceCallbacks( + Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks, + Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) { + final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class); + + doCallRealMethod().when(service).setService(any(PrintService.class)); + when(service.getService()).thenCallRealMethod(); + + if (onCreatePrinterDiscoverySessionCallbacks != null) { + doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service) + .onCreatePrinterDiscoverySessionCallbacks(); + } + if (onPrintJobQueued != null) { + doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class)); + } + if (onRequestCancelPrintJob != null) { + doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob( + any(PrintJob.class)); + } + + return service; + } + + protected final class CallCounter { + private final Object mLock = new Object(); + + private int mCallCount; + + public void call() { + synchronized (mLock) { + mCallCount++; + mLock.notifyAll(); + } + } + + public int getCallCount() { + synchronized (mLock) { + return mCallCount; + } + } + + public void waitForCount(int count, long timeoutMillis) throws TimeoutException { + synchronized (mLock) { + final long startTimeMillis = SystemClock.uptimeMillis(); + while (mCallCount < count) { + try { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis; + if (remainingTimeMillis <= 0) { + throw new TimeoutException(); + } + mLock.wait(timeoutMillis); + } catch (InterruptedException ie) { + /* ignore */ + } + } + } + } + } + + protected boolean supportsPrinting() { + return getInstrumentation().getContext().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_PRINTING); + } +} diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java new file mode 100644 index 000000000000..5179b42fd650 --- /dev/null +++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2016 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 android.print; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.Looper; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.print.IPrintDocumentAdapter; +import android.print.IPrintJobStateChangeListener; +import android.print.IPrintManager; +import android.print.IPrinterDiscoveryObserver; +import android.print.PageRange; +import android.print.PrintAttributes; +import android.print.PrintAttributes.Margins; +import android.print.PrintAttributes.MediaSize; +import android.print.PrintAttributes.Resolution; +import android.print.PrintDocumentAdapter; +import android.print.PrintJob; +import android.print.PrintJobId; +import android.print.PrintJobInfo; +import android.print.PrintManager; +import android.print.PrinterCapabilitiesInfo; +import android.print.PrinterDiscoverySession; +import android.print.PrinterId; +import android.print.PrinterInfo; +import android.printservice.PrintServiceInfo; + +import android.print.mockservice.MockPrintService; +import android.print.mockservice.PrintServiceCallbacks; +import android.print.mockservice.PrinterDiscoverySessionCallbacks; +import android.print.mockservice.StubbablePrinterDiscoverySession; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.List; + +/** + * tests feeding all possible parameters to the IPrintManager Binder. + */ +public class IPrintManagerParametersTest extends BasePrintTest { + + private final int BAD_APP_ID = 0xffffffff; + + private final int mAppId; + private final int mUserId; + private final PrintJobId mBadPrintJobId; + + private PrintJob mGoodPrintJob; + private PrinterId mBadPrinterId; + private PrinterId mGoodPrinterId; + private ComponentName mGoodComponentName; + private ComponentName mBadComponentName; + + private IPrintManager mIPrintManager; + + /** + * Create a new IPrintManagerParametersTest and setup basic fields. + */ + public IPrintManagerParametersTest() { + super(); + + mAppId = UserHandle.getAppId(Process.myUid()); + mUserId = UserHandle.myUserId(); + mBadPrintJobId = new PrintJobId(); + mBadComponentName = new ComponentName("bad", "bad"); + } + + /** + * Create a mock PrintDocumentAdapter. + * + * @return The adapter + */ + private @NonNull PrintDocumentAdapter createMockAdapter() { + return new PrintDocumentAdapter() { + @Override + public void onStart() { + onStartCalled(); + } + + @Override + public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, + CancellationSignal cancellationSignal, LayoutResultCallback callback, + Bundle extras) { + } + + @Override + public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, + CancellationSignal cancellationSignal, WriteResultCallback callback) { + } + }; + } + + /** + * Create mock print service callbacks. + * + * @return the callbacks + */ + private PrintServiceCallbacks createMockCallbacks() { + return createMockPrintServiceCallbacks( + new Answer<PrinterDiscoverySessionCallbacks>() { + @Override + public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) { + return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) { + // Get the session. + StubbablePrinterDiscoverySession session = + ((PrinterDiscoverySessionCallbacks) invocation + .getMock()).getSession(); + + if (session.getPrinters().isEmpty()) { + final String PRINTER_NAME = "good printer"; + List<PrinterInfo> printers = new ArrayList<PrinterInfo>(); + + // Add the printer. + mGoodPrinterId = session.getService() + .generatePrinterId(PRINTER_NAME); + + PrinterCapabilitiesInfo capabilities = + new PrinterCapabilitiesInfo.Builder(mGoodPrinterId) + .setMinMargins( + new Margins(200, 200, 200, 200)) + .addMediaSize(MediaSize.ISO_A4, true) + .addResolution(new Resolution("300x300", + "300x300", 300, 300), + true) + .setColorModes( + PrintAttributes.COLOR_MODE_COLOR, + PrintAttributes.COLOR_MODE_COLOR) + .build(); + + PrinterInfo printer = new PrinterInfo.Builder( + mGoodPrinterId, + PRINTER_NAME, + PrinterInfo.STATUS_IDLE) + .setCapabilities(capabilities) + .build(); + printers.add(printer); + + session.addPrinters(printers); + } + onPrinterDiscoverySessionStartCalled(); + return null; + } + }, null, null, null, null, null, null); + } + }, + null, null); + } + + /** + * Create a IPrintJobStateChangeListener object. + * + * @return the object + * @throws Exception if the object could not be created. + */ + private IPrintJobStateChangeListener createMockIPrintJobStateChangeListener() throws Exception { + return new PrintManager.PrintJobStateChangeListenerWrapper(null, + new Handler(Looper.getMainLooper())); + } + + /** + * Create a IPrinterDiscoveryObserver object. + * + * @return the object + * @throws Exception if the object could not be created. + */ + private IPrinterDiscoveryObserver createMockIPrinterDiscoveryObserver() throws Exception { + return new PrinterDiscoverySession.PrinterDiscoveryObserver(null); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + + MockPrintService.setCallbacks(createMockCallbacks()); + + mGoodComponentName = getActivity().getComponentName(); + + mGoodPrintJob = print(createMockAdapter(), null); + + mIPrintManager = IPrintManager.Stub + .asInterface(ServiceManager.getService(Context.PRINT_SERVICE)); + + // Generate dummy printerId which is a valid PrinterId object, but does not correspond to a + // printer + mBadPrinterId = new PrinterId(mGoodComponentName, "dummy printer"); + + // Wait for PrintActivity to be ready + waitForStartAdapterCallbackCalled(); + + // Wait for printer discovery session to be ready + waitForPrinterDiscoverySessionStartCallbackCalled(); + } + + /** + * {@link Runnable} that can throw and {@link Exception} + */ + private interface Invokable { + /** + * Execute the {@link Invokable} + * + * @throws Exception + */ + public void run() throws Exception; + } + + /** + * Assert that the invokable throws an expectedException + * + * @param invokable The {@link Invokable} to run + * @param expectedClass The {@link Exception} that is supposed to be thrown + */ + public void assertException(Invokable invokable, Class<? extends Exception> expectedClass) + throws Exception { + try { + invokable.run(); + } catch (Exception e) { + if (e.getClass().isAssignableFrom(expectedClass)) { + return; + } else { + throw new AssertionError("Expected: " + expectedClass.getName() + ", got: " + + e.getClass().getName()); + } + } + + throw new AssertionError("No exception thrown"); + } + + /** + * test IPrintManager.getPrintJobInfo + */ + public void testGetPrintJobInfo() throws Exception { + assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(), + mAppId, mUserId).getId()); + assertEquals(null, mIPrintManager.getPrintJobInfo(mBadPrintJobId, mAppId, mUserId)); + assertEquals(null, mIPrintManager.getPrintJobInfo(null, mAppId, mUserId)); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(), BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.getPrintJobInfos + */ + public void testGetPrintJobInfos() throws Exception { + List<PrintJobInfo> infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId); + + boolean foundPrintJob = false; + for (PrintJobInfo info : infos) { + if (info.getId().equals(mGoodPrintJob.getId())) { + assertEquals(PrintJobInfo.STATE_CREATED, info.getState()); + foundPrintJob = true; + } + } + assertTrue(foundPrintJob); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.getPrintJobInfos(BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.print + */ + public void testPrint() throws Exception { + final String name = "dummy print job"; + + final IPrintDocumentAdapter adapter = new PrintManager + .PrintDocumentAdapterDelegate(getActivity(), createMockAdapter()); + + // Valid parameters are tested in setUp() + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.print(null, adapter, null, mGoodComponentName.getPackageName(), + mAppId, mUserId); + } + }, IllegalArgumentException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.print(name, null, null, mGoodComponentName.getPackageName(), + mAppId, mUserId); + } + }, NullPointerException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.print(name, adapter, null, null, mAppId, mUserId); + } + }, IllegalArgumentException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.print(name, adapter, null, mBadComponentName.getPackageName(), + mAppId, mUserId); + } + }, IllegalArgumentException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.print(name, adapter, null, mGoodComponentName.getPackageName(), + BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.cancelPrintJob + */ + public void testCancelPrintJob() throws Exception { + // Invalid print jobs IDs do not produce an exception + mIPrintManager.cancelPrintJob(mBadPrintJobId, mAppId, mUserId); + mIPrintManager.cancelPrintJob(null, mAppId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + + // Must be last as otherwise mGoodPrintJob will not be good anymore + mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), mAppId, mUserId); + } + + /** + * test IPrintManager.restartPrintJob + */ + public void testRestartPrintJob() throws Exception { + mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId); + + // Invalid print jobs IDs do not produce an exception + mIPrintManager.restartPrintJob(mBadPrintJobId, mAppId, mUserId); + mIPrintManager.restartPrintJob(null, mAppId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.addPrintJobStateChangeListener + */ + public void testAddPrintJobStateChangeListener() throws Exception { + final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener(); + + mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.addPrintJobStateChangeListener(null, mAppId, mUserId); + } + }, NullPointerException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.addPrintJobStateChangeListener(listener, BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.removePrintJobStateChangeListener + */ + public void testRemovePrintJobStateChangeListener() throws Exception { + final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener(); + + mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId); + mIPrintManager.removePrintJobStateChangeListener(listener, mUserId); + + // Removing unknown listeners is a no-op + mIPrintManager.removePrintJobStateChangeListener(listener, mUserId); + + mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId); + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.removePrintJobStateChangeListener(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.getInstalledPrintServices + */ + public void testGetInstalledPrintServices() throws Exception { + List<PrintServiceInfo> printServices = mIPrintManager.getInstalledPrintServices(mUserId); + assertTrue(printServices.size() >= 2); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.getEnabledPrintServices + */ + public void testGetEnabledPrintServices() throws Exception { + List<PrintServiceInfo> printServices = mIPrintManager.getEnabledPrintServices(mUserId); + assertTrue(printServices.size() >= 2); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.createPrinterDiscoverySession + */ + public void testCreatePrinterDiscoverySession() throws Exception { + final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); + + mIPrintManager.createPrinterDiscoverySession(listener, mUserId); + + try { + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.createPrinterDiscoverySession(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } finally { + // Remove discovery session so that the next test create a new one. Usually a leaked + // session is removed on the next call from the print service. But in this case we want + // to force a new call to onPrinterDiscoverySessionStart in the next test. + mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId); + } + } + + /** + * test IPrintManager.startPrinterDiscovery + */ + public void testStartPrinterDiscovery() throws Exception { + final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); + final List<PrinterId> goodPrinters = new ArrayList<>(); + goodPrinters.add(mGoodPrinterId); + + final List<PrinterId> badPrinters = new ArrayList<>(); + badPrinters.add(mBadPrinterId); + + final List<PrinterId> emptyPrinters = new ArrayList<>(); + + final List<PrinterId> nullPrinters = new ArrayList<>(); + nullPrinters.add(null); + + mIPrintManager.startPrinterDiscovery(listener, goodPrinters, mUserId); + + // Bad or no printers do no cause exceptions + mIPrintManager.startPrinterDiscovery(listener, badPrinters, mUserId); + mIPrintManager.startPrinterDiscovery(listener, emptyPrinters, mUserId); + mIPrintManager.startPrinterDiscovery(listener, null, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.startPrinterDiscovery(listener, nullPrinters, mUserId); + } + }, NullPointerException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.startPrinterDiscovery(null, goodPrinters, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.stopPrinterDiscovery + */ + public void testStopPrinterDiscovery() throws Exception { + final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); + + mIPrintManager.startPrinterDiscovery(listener, null, mUserId); + mIPrintManager.stopPrinterDiscovery(listener, mUserId); + + // Stopping an already stopped session is a no-op + mIPrintManager.stopPrinterDiscovery(listener, mUserId); + + mIPrintManager.startPrinterDiscovery(listener, null, mUserId); + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.stopPrinterDiscovery(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.validatePrinters + */ + public void testValidatePrinters() throws Exception { + final List<PrinterId> goodPrinters = new ArrayList<>(); + goodPrinters.add(mGoodPrinterId); + + final List<PrinterId> badPrinters = new ArrayList<>(); + badPrinters.add(mBadPrinterId); + + final List<PrinterId> emptyPrinters = new ArrayList<>(); + + final List<PrinterId> nullPrinters = new ArrayList<>(); + nullPrinters.add(null); + + mIPrintManager.validatePrinters(goodPrinters, mUserId); + + // Bad or empty list of printers do no cause exceptions + mIPrintManager.validatePrinters(badPrinters, mUserId); + mIPrintManager.validatePrinters(emptyPrinters, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.validatePrinters(null, mUserId); + } + }, NullPointerException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.validatePrinters(nullPrinters, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.startPrinterStateTracking + */ + public void testStartPrinterStateTracking() throws Exception { + mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId); + + // Bad printers do no cause exceptions + mIPrintManager.startPrinterStateTracking(mBadPrinterId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.startPrinterStateTracking(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.getCustomPrinterIcon + */ + public void testGetCustomPrinterIcon() throws Exception { + mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId); + + // Bad printers do no cause exceptions + mIPrintManager.getCustomPrinterIcon(mBadPrinterId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.getCustomPrinterIcon(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.stopPrinterStateTracking + */ + public void testStopPrinterStateTracking() throws Exception { + mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId); + mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId); + + // Stop to track a non-tracked printer is a no-op + mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId); + + // Bad printers do no cause exceptions + mIPrintManager.startPrinterStateTracking(mBadPrinterId, mUserId); + mIPrintManager.stopPrinterStateTracking(mBadPrinterId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.stopPrinterStateTracking(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.destroyPrinterDiscoverySession + */ + public void testDestroyPrinterDiscoverySession() throws Exception { + final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); + + mIPrintManager.createPrinterDiscoverySession(listener, mUserId); + mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId); + + // Destroying already destroyed session is a no-op + mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.destroyPrinterDiscoverySession(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } +} diff --git a/core/tests/coretests/src/android/print/PrintTestActivity.java b/core/tests/coretests/src/android/print/PrintTestActivity.java new file mode 100644 index 000000000000..86074a62eab6 --- /dev/null +++ b/core/tests/coretests/src/android/print/PrintTestActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 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 android.print; + +import android.app.Activity; +import android.os.Bundle; +import android.view.WindowManager; + +public class PrintTestActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON + | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } +} diff --git a/core/tests/coretests/src/android/print/mockservice/MockPrintService.java b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java new file mode 100644 index 000000000000..9c11c22282d7 --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 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 android.print.mockservice; + +public class MockPrintService extends StubbablePrintService { + + private static final Object sLock = new Object(); + + private static PrintServiceCallbacks sCallbacks; + + public static void setCallbacks(PrintServiceCallbacks callbacks) { + synchronized (sLock) { + sCallbacks = callbacks; + } + } + + @Override + protected PrintServiceCallbacks getCallbacks() { + synchronized (sLock) { + if (sCallbacks != null) { + sCallbacks.setService(this); + } + return sCallbacks; + } + } +} diff --git a/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java new file mode 100644 index 000000000000..4e892072f0cb --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 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 android.print.mockservice; + +import android.printservice.PrintJob; +import android.printservice.PrintService; + +public abstract class PrintServiceCallbacks { + + private PrintService mService; + + public PrintService getService() { + return mService; + } + + public void setService(PrintService service) { + mService = service; + } + + public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks(); + + public abstract void onRequestCancelPrintJob(PrintJob printJob); + + public abstract void onPrintJobQueued(PrintJob printJob); +} diff --git a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java new file mode 100644 index 000000000000..26b7caeba15f --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 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 android.print.mockservice; + +import android.print.PrinterId; +import android.printservice.CustomPrinterIconCallback; + +import java.util.List; + +public abstract class PrinterDiscoverySessionCallbacks { + + private StubbablePrinterDiscoverySession mSession; + + public void setSession(StubbablePrinterDiscoverySession session) { + mSession = session; + } + + public StubbablePrinterDiscoverySession getSession() { + return mSession; + } + + public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList); + + public abstract void onStopPrinterDiscovery(); + + public abstract void onValidatePrinters(List<PrinterId> printerIds); + + public abstract void onStartPrinterStateTracking(PrinterId printerId); + + public abstract void onRequestCustomPrinterIcon(PrinterId printerId, + CustomPrinterIconCallback callback); + + public abstract void onStopPrinterStateTracking(PrinterId printerId); + + public abstract void onDestroy(); +} diff --git a/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java b/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java new file mode 100644 index 000000000000..fb76e6780374 --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 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 android.print.mockservice; + +import android.app.Activity; +import android.os.Bundle; + +public class SettingsActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } +} diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java new file mode 100644 index 000000000000..b58b27350c28 --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 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 android.print.mockservice; + +import android.printservice.PrintJob; +import android.printservice.PrintService; +import android.printservice.PrinterDiscoverySession; + +public abstract class StubbablePrintService extends PrintService { + + @Override + public PrinterDiscoverySession onCreatePrinterDiscoverySession() { + PrintServiceCallbacks callbacks = getCallbacks(); + if (callbacks != null) { + return new StubbablePrinterDiscoverySession(this, + getCallbacks().onCreatePrinterDiscoverySessionCallbacks()); + } + return null; + } + + @Override + public void onRequestCancelPrintJob(PrintJob printJob) { + PrintServiceCallbacks callbacks = getCallbacks(); + if (callbacks != null) { + callbacks.onRequestCancelPrintJob(printJob); + } + } + + @Override + public void onPrintJobQueued(PrintJob printJob) { + PrintServiceCallbacks callbacks = getCallbacks(); + if (callbacks != null) { + callbacks.onPrintJobQueued(printJob); + } + } + + protected abstract PrintServiceCallbacks getCallbacks(); +} diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java new file mode 100644 index 000000000000..04683f267aab --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2016 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 android.print.mockservice; + +import android.print.PrinterId; +import android.printservice.CustomPrinterIconCallback; +import android.printservice.PrintService; +import android.printservice.PrinterDiscoverySession; + +import java.util.List; + +public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession { + private final PrintService mService; + private final PrinterDiscoverySessionCallbacks mCallbacks; + + public StubbablePrinterDiscoverySession(PrintService service, + PrinterDiscoverySessionCallbacks callbacks) { + mService = service; + mCallbacks = callbacks; + if (mCallbacks != null) { + mCallbacks.setSession(this); + } + } + + public PrintService getService() { + return mService; + } + + @Override + public void onStartPrinterDiscovery(List<PrinterId> priorityList) { + if (mCallbacks != null) { + mCallbacks.onStartPrinterDiscovery(priorityList); + } + } + + @Override + public void onStopPrinterDiscovery() { + if (mCallbacks != null) { + mCallbacks.onStopPrinterDiscovery(); + } + } + + @Override + public void onValidatePrinters(List<PrinterId> printerIds) { + if (mCallbacks != null) { + mCallbacks.onValidatePrinters(printerIds); + } + } + + @Override + public void onStartPrinterStateTracking(PrinterId printerId) { + if (mCallbacks != null) { + mCallbacks.onStartPrinterStateTracking(printerId); + } + } + + @Override + public void onRequestCustomPrinterIcon(PrinterId printerId, + CustomPrinterIconCallback callback) { + if (mCallbacks != null) { + mCallbacks.onRequestCustomPrinterIcon(printerId, callback); + } + } + + @Override + public void onStopPrinterStateTracking(PrinterId printerId) { + if (mCallbacks != null) { + mCallbacks.onStopPrinterStateTracking(printerId); + } + } + + @Override + public void onDestroy() { + if (mCallbacks != null) { + mCallbacks.onDestroy(); + } + } +} diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java index cd30e263a65d..46a2098ad5da 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java @@ -16,6 +16,7 @@ package com.android.printspooler.ui; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Loader; @@ -452,7 +453,7 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>> return mPersistenceManager.mReadHistoryCompleted; } - public void setTrackedPrinter(PrinterId printerId) { + public void setTrackedPrinter(@Nullable PrinterId printerId) { if (isStarted() && mDiscoverySession != null && mDiscoverySession.isPrinterDiscoveryStarted()) { if (mTrackedPrinter != null) { diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java index cbc568ae1e11..6d60bb86cd7e 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java @@ -16,6 +16,7 @@ package com.android.printspooler.ui; +import android.annotation.Nullable; import android.app.Activity; import android.app.LoaderManager.LoaderCallbacks; import android.content.Loader; @@ -89,7 +90,7 @@ public class PrinterRegistry { return false; } - public void setTrackedPrinter(PrinterId printerId) { + public void setTrackedPrinter(@Nullable PrinterId printerId) { FusedPrintersProvider provider = getPrinterProvider(); if (provider != null) { provider.setTrackedPrinter(printerId); diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java index 5abb6e7d9367..e6f41775111c 100644 --- a/services/print/java/com/android/server/print/PrintManagerService.java +++ b/services/print/java/com/android/server/print/PrintManagerService.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.GET_SERVICES; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import android.Manifest; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.content.ComponentName; @@ -47,11 +48,11 @@ import android.print.PrintJobInfo; import android.print.PrinterId; import android.printservice.PrintServiceInfo; import android.provider.Settings; -import android.text.TextUtils; import android.util.SparseArray; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.Preconditions; import com.android.server.SystemService; import java.io.FileDescriptor; @@ -108,6 +109,10 @@ public final class PrintManagerService extends SystemService { @Override public Bundle print(String printJobName, IPrintDocumentAdapter adapter, PrintAttributes attributes, String packageName, int appId, int userId) { + printJobName = Preconditions.checkStringNotEmpty(printJobName); + adapter = Preconditions.checkNotNull(adapter); + packageName = Preconditions.checkStringNotEmpty(packageName); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final int resolvedAppId; final UserState userState; @@ -153,6 +158,10 @@ public final class PrintManagerService extends SystemService { @Override public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId, int userId) { + if (printJobId == null) { + return null; + } + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final int resolvedAppId; final UserState userState; @@ -174,6 +183,8 @@ public final class PrintManagerService extends SystemService { @Override public Icon getCustomPrinterIcon(PrinterId printerId, int userId) { + printerId = Preconditions.checkNotNull(printerId); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -193,6 +204,10 @@ public final class PrintManagerService extends SystemService { @Override public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) { + if (printJobId == null) { + return; + } + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final int resolvedAppId; final UserState userState; @@ -214,6 +229,10 @@ public final class PrintManagerService extends SystemService { @Override public void restartPrintJob(PrintJobId printJobId, int appId, int userId) { + if (printJobId == null) { + return; + } + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final int resolvedAppId; final UserState userState; @@ -279,6 +298,8 @@ public final class PrintManagerService extends SystemService { @Override public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer, int userId) { + observer = Preconditions.checkNotNull(observer); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -299,6 +320,8 @@ public final class PrintManagerService extends SystemService { @Override public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer, int userId) { + observer = Preconditions.checkNotNull(observer); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -319,6 +342,12 @@ public final class PrintManagerService extends SystemService { @Override public void startPrinterDiscovery(IPrinterDiscoveryObserver observer, List<PrinterId> priorityList, int userId) { + observer = Preconditions.checkNotNull(observer); + if (priorityList != null) { + priorityList = Preconditions.checkCollectionElementsNotNull(priorityList, + "PrinterId"); + } + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -338,6 +367,8 @@ public final class PrintManagerService extends SystemService { @Override public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) { + observer = Preconditions.checkNotNull(observer); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -357,6 +388,8 @@ public final class PrintManagerService extends SystemService { @Override public void validatePrinters(List<PrinterId> printerIds, int userId) { + printerIds = Preconditions.checkCollectionElementsNotNull(printerIds, "PrinterId"); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -376,6 +409,8 @@ public final class PrintManagerService extends SystemService { @Override public void startPrinterStateTracking(PrinterId printerId, int userId) { + printerId = Preconditions.checkNotNull(printerId); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -395,6 +430,8 @@ public final class PrintManagerService extends SystemService { @Override public void stopPrinterStateTracking(PrinterId printerId, int userId) { + printerId = Preconditions.checkNotNull(printerId); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -415,6 +452,8 @@ public final class PrintManagerService extends SystemService { @Override public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener, int appId, int userId) throws RemoteException { + listener = Preconditions.checkNotNull(listener); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final int resolvedAppId; final UserState userState; @@ -437,6 +476,8 @@ public final class PrintManagerService extends SystemService { @Override public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener, int userId) { + listener = Preconditions.checkNotNull(listener); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -456,6 +497,9 @@ public final class PrintManagerService extends SystemService { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + fd = Preconditions.checkNotNull(fd); + pw = Preconditions.checkNotNull(pw); + if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump PrintManager from from pid=" @@ -707,10 +751,8 @@ public final class PrintManagerService extends SystemService { return userId; } - private String resolveCallingPackageNameEnforcingSecurity(String packageName) { - if (TextUtils.isEmpty(packageName)) { - return null; - } + private @NonNull String resolveCallingPackageNameEnforcingSecurity( + @NonNull String packageName) { String[] packages = mContext.getPackageManager().getPackagesForUid( Binder.getCallingUid()); final int packageCount = packages.length; @@ -719,7 +761,7 @@ public final class PrintManagerService extends SystemService { return packageName; } } - return null; + throw new IllegalArgumentException("packageName has to belong to the caller"); } private int getCurrentUserId () { diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java index 5ee4066f69ec..9b99c677a822 100644 --- a/services/print/java/com/android/server/print/RemotePrintService.java +++ b/services/print/java/com/android/server/print/RemotePrintService.java @@ -409,7 +409,7 @@ final class RemotePrintService implements DeathRecipient { } } - public void startPrinterStateTracking(PrinterId printerId) { + public void startPrinterStateTracking(@NonNull PrinterId printerId) { mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING, printerId).sendToTarget(); } @@ -420,7 +420,7 @@ final class RemotePrintService implements DeathRecipient { * @param printerId the id of the printer the icon should be loaded for * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() */ - public void requestCustomPrinterIcon(PrinterId printerId) { + public void requestCustomPrinterIcon(@NonNull PrinterId printerId) { try { if (isBound()) { mPrintService.requestCustomPrinterIcon(printerId); @@ -430,7 +430,7 @@ final class RemotePrintService implements DeathRecipient { } } - private void handleStartPrinterStateTracking(final PrinterId printerId) { + private void handleStartPrinterStateTracking(final @NonNull PrinterId printerId) { throwIfDestroyed(); // Take a note we are tracking the printer. if (mTrackedPrinterList == null) { diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index 78edc4df08fa..fcf2fc8e7860 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.GET_META_DATA; import static android.content.pm.PackageManager.GET_SERVICES; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; import android.content.ComponentName; @@ -176,8 +177,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } @SuppressWarnings("deprecation") - public Bundle print(String printJobName, IPrintDocumentAdapter adapter, - PrintAttributes attributes, String packageName, int appId) { + public Bundle print(@NonNull String printJobName, @NonNull IPrintDocumentAdapter adapter, + @Nullable PrintAttributes attributes, @NonNull String packageName, int appId) { // Create print job place holder. final PrintJobInfo printJob = new PrintJobInfo(); printJob.setId(new PrintJobId()); @@ -267,7 +268,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { return new ArrayList<PrintJobInfo>(result.values()); } - public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) { + public PrintJobInfo getPrintJobInfo(@NonNull PrintJobId printJobId, int appId) { PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId); if (printJob == null) { printJob = mSpooler.getPrintJobInfo(printJobId, appId); @@ -290,7 +291,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { * not yet available * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() */ - public Icon getCustomPrinterIcon(PrinterId printerId) { + public @Nullable Icon getCustomPrinterIcon(@NonNull PrinterId printerId) { Icon icon = mSpooler.getCustomPrinterIcon(printerId); if (icon == null) { @@ -303,7 +304,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { return icon; } - public void cancelPrintJob(PrintJobId printJobId, int appId) { + public void cancelPrintJob(@NonNull PrintJobId printJobId, int appId) { PrintJobInfo printJobInfo = mSpooler.getPrintJobInfo(printJobId, appId); if (printJobInfo == null) { return; @@ -313,15 +314,19 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { mSpooler.setPrintJobCancelling(printJobId, true); if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) { - ComponentName printServiceName = printJobInfo.getPrinterId().getServiceName(); - RemotePrintService printService = null; - synchronized (mLock) { - printService = mActiveServices.get(printServiceName); - } - if (printService == null) { - return; + PrinterId printerId = printJobInfo.getPrinterId(); + + if (printerId != null) { + ComponentName printServiceName = printerId.getServiceName(); + RemotePrintService printService = null; + synchronized (mLock) { + printService = mActiveServices.get(printServiceName); + } + if (printService == null) { + return; + } + printService.onRequestCancelPrintJob(printJobInfo); } - printService.onRequestCancelPrintJob(printJobInfo); } else { // If the print job is failed we do not need cooperation // from the print service. @@ -329,7 +334,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void restartPrintJob(PrintJobId printJobId, int appId) { + public void restartPrintJob(@NonNull PrintJobId printJobId, int appId) { PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId); if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) { return; @@ -363,7 +368,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) { + public void createPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) { synchronized (mLock) { throwIfDestroyedLocked(); @@ -386,7 +391,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer) { + public void destroyPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) { synchronized (mLock) { // Already destroyed - nothing to do. if (mPrinterDiscoverySession == null) { @@ -397,8 +402,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void startPrinterDiscovery(IPrinterDiscoveryObserver observer, - List<PrinterId> printerIds) { + public void startPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer, + @Nullable List<PrinterId> printerIds) { synchronized (mLock) { throwIfDestroyedLocked(); // No services - nothing to do. @@ -415,7 +420,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer) { + public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) { synchronized (mLock) { throwIfDestroyedLocked(); // No services - nothing to do. @@ -431,7 +436,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void validatePrinters(List<PrinterId> printerIds) { + public void validatePrinters(@NonNull List<PrinterId> printerIds) { synchronized (mLock) { throwIfDestroyedLocked(); // No services - nothing to do. @@ -447,7 +452,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void startPrinterStateTracking(PrinterId printerId) { + public void startPrinterStateTracking(@NonNull PrinterId printerId) { synchronized (mLock) { throwIfDestroyedLocked(); // No services - nothing to do. @@ -479,7 +484,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener, + public void addPrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener, int appId) throws RemoteException { synchronized (mLock) { throwIfDestroyedLocked(); @@ -497,7 +502,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener) { + public void removePrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener) { synchronized (mLock) { throwIfDestroyedLocked(); if (mPrintJobStateChangeListenerRecords == null) { @@ -613,7 +618,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { mDestroyed = true; } - public void dump(FileDescriptor fd, PrintWriter pw, String prefix) { + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) { pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":"); pw.println(); @@ -991,10 +996,10 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient { - final IPrintJobStateChangeListener listener; + @NonNull final IPrintJobStateChangeListener listener; final int appId; - public PrintJobStateChangeListenerRecord(IPrintJobStateChangeListener listener, + public PrintJobStateChangeListenerRecord(@NonNull IPrintJobStateChangeListener listener, int appId) throws RemoteException { this.listener = listener; this.appId = appId; @@ -1043,7 +1048,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { .sendToTarget(); } - public void addObserverLocked(IPrinterDiscoveryObserver observer) { + public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { // Add the observer. mDiscoveryObservers.register(observer); @@ -1058,7 +1063,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void removeObserverLocked(IPrinterDiscoveryObserver observer) { + public void removeObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { // Remove the observer. mDiscoveryObservers.unregister(observer); // No one else observing - then kill it. @@ -1067,8 +1072,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public final void startPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer, - List<PrinterId> priorityList) { + public final void startPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer, + @Nullable List<PrinterId> priorityList) { if (mIsDestroyed) { Log.w(LOG_TAG, "Not starting dicovery - session destroyed"); return; @@ -1101,7 +1106,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { .sendToTarget(); } - public final void stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer) { + public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) { if (mIsDestroyed) { Log.w(LOG_TAG, "Not stopping dicovery - session destroyed"); return; @@ -1121,7 +1126,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { .sendToTarget(); } - public void validatePrintersLocked(List<PrinterId> printerIds) { + public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) { if (mIsDestroyed) { Log.w(LOG_TAG, "Not validating pritners - session destroyed"); return; @@ -1135,13 +1140,15 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { 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(); + if (printerId != null) { + 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. @@ -1157,7 +1164,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public final void startPrinterStateTrackingLocked(PrinterId printerId) { + public final void startPrinterStateTrackingLocked(@NonNull PrinterId printerId) { if (mIsDestroyed) { Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed"); return; @@ -1500,8 +1507,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { service.validatePrinters(printerIds); } - private void handleStartPrinterStateTracking(RemotePrintService service, - PrinterId printerId) { + private void handleStartPrinterStateTracking(@NonNull RemotePrintService service, + @NonNull PrinterId printerId) { service.startPrinterStateTracking(printerId); } |