Merge "Check parameters for callers of IPrintManager"

This commit is contained in:
Philip P. Moltmann
2016-01-26 22:52:58 +00:00
committed by Android (Google) Code Review
21 changed files with 1545 additions and 63 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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(),

View File

@@ -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");
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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"/>

View File

@@ -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);
}
}

View File

@@ -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
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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();
}
}
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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 () {

View File

@@ -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) {

View File

@@ -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);
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);
}
if (printService == null) {
return;
}
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);
}