1. Factored out the printer discovery APIs of a print service in a dedicated session object that is created by the print service on demand. This ensures that added/removed/updated printers from one session do not interfere with another session. 2. Updated the app facing APIs to pass in a document info along with a printed file. Also exposed the print file adapter so apps that create a temporary file for printing can intercept when it is read by the system so the file can be deleted. 3. Updated the print service documentation. Change-Id: I3473d586c26d8bda1cf7e2bdacb441aa9df982ed
258 lines
8.8 KiB
Java
258 lines
8.8 KiB
Java
/*
|
|
* Copyright (C) 2013 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 com.android.server.print;
|
|
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.print.IPrinterDiscoverySessionObserver;
|
|
import android.print.PrintJobInfo;
|
|
import android.printservice.PrintServiceInfo;
|
|
import android.provider.Settings;
|
|
import android.text.TextUtils;
|
|
import android.text.TextUtils.SimpleStringSplitter;
|
|
import android.util.Slog;
|
|
|
|
import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Represents the print state for a user.
|
|
*/
|
|
final class UserState implements PrintSpoolerCallbacks {
|
|
|
|
private static final String LOG_TAG = "UserState";
|
|
|
|
private static final char COMPONENT_NAME_SEPARATOR = ':';
|
|
|
|
private final SimpleStringSplitter mStringColonSplitter =
|
|
new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
|
|
|
|
private final Intent mQueryIntent =
|
|
new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
|
|
|
|
private final Map<ComponentName, RemotePrintService> mActiveServices =
|
|
new HashMap<ComponentName, RemotePrintService>();
|
|
|
|
private final List<PrintServiceInfo> mInstalledServices =
|
|
new ArrayList<PrintServiceInfo>();
|
|
|
|
private final Set<ComponentName> mEnabledServices =
|
|
new HashSet<ComponentName>();
|
|
|
|
private final Object mLock;
|
|
|
|
private final Context mContext;
|
|
|
|
private final int mUserId;
|
|
|
|
private final RemotePrintSpooler mSpooler;
|
|
|
|
private boolean mDestroyed;
|
|
|
|
public UserState(Context context, int userId, Object lock) {
|
|
mContext = context;
|
|
mUserId = userId;
|
|
mLock = lock;
|
|
mSpooler = new RemotePrintSpooler(context, userId, this);
|
|
}
|
|
|
|
@Override
|
|
public void onPrintJobQueued(PrintJobInfo printJob) {
|
|
final RemotePrintService service;
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
ComponentName printServiceName = printJob.getPrinterId().getServiceName();
|
|
service = mActiveServices.get(printServiceName);
|
|
}
|
|
if (service != null) {
|
|
service.onPrintJobQueued(printJob);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAllPrintJobsForServiceHandled(ComponentName printService) {
|
|
final RemotePrintService service;
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
service = mActiveServices.get(printService);
|
|
}
|
|
if (service != null) {
|
|
service.onAllPrintJobsHandled();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
|
|
final List<RemotePrintService> services;
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
if (mActiveServices.isEmpty()) {
|
|
return;
|
|
}
|
|
services = new ArrayList<RemotePrintService>(mActiveServices.values());
|
|
}
|
|
final int serviceCount = services.size();
|
|
for (int i = 0; i < serviceCount; i++) {
|
|
RemotePrintService service = services.get(i);
|
|
service.createPrinterDiscoverySession(observer);
|
|
}
|
|
}
|
|
|
|
public void updateIfNeededLocked() {
|
|
throwIfDestroyedLocked();
|
|
if (readConfigurationLocked()) {
|
|
onConfigurationChangedLocked();
|
|
}
|
|
}
|
|
|
|
public RemotePrintSpooler getSpoolerLocked() {
|
|
throwIfDestroyedLocked();
|
|
return mSpooler;
|
|
}
|
|
|
|
public Map<ComponentName, RemotePrintService> getActiveServicesLocked() {
|
|
synchronized(mLock) {
|
|
throwIfDestroyedLocked();
|
|
return mActiveServices;
|
|
}
|
|
}
|
|
|
|
public Set<ComponentName> getEnabledServices() {
|
|
synchronized(mLock) {
|
|
throwIfDestroyedLocked();
|
|
return mEnabledServices;
|
|
}
|
|
}
|
|
|
|
public void destroyLocked() {
|
|
throwIfDestroyedLocked();
|
|
mSpooler.destroy();
|
|
for (RemotePrintService service : mActiveServices.values()) {
|
|
service.destroy();
|
|
}
|
|
mActiveServices.clear();
|
|
mInstalledServices.clear();
|
|
mEnabledServices.clear();
|
|
mDestroyed = true;
|
|
}
|
|
|
|
private boolean readConfigurationLocked() {
|
|
boolean somethingChanged = false;
|
|
somethingChanged |= readInstalledPrintServicesLocked();
|
|
somethingChanged |= readEnabledPrintServicesLocked();
|
|
return somethingChanged;
|
|
}
|
|
|
|
private boolean readInstalledPrintServicesLocked() {
|
|
Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
|
|
|
|
List<ResolveInfo> installedServices = mContext.getPackageManager()
|
|
.queryIntentServicesAsUser(mQueryIntent, PackageManager.GET_SERVICES
|
|
| PackageManager.GET_META_DATA, mUserId);
|
|
|
|
final int installedCount = installedServices.size();
|
|
for (int i = 0, count = installedCount; i < count; i++) {
|
|
ResolveInfo installedService = installedServices.get(i);
|
|
if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals(
|
|
installedService.serviceInfo.permission)) {
|
|
ComponentName serviceName = new ComponentName(
|
|
installedService.serviceInfo.packageName,
|
|
installedService.serviceInfo.name);
|
|
Slog.w(LOG_TAG, "Skipping print service "
|
|
+ serviceName.flattenToShortString()
|
|
+ " since it does not require permission "
|
|
+ android.Manifest.permission.BIND_PRINT_SERVICE);
|
|
continue;
|
|
}
|
|
tempPrintServices.add(PrintServiceInfo.create(installedService, mContext));
|
|
}
|
|
|
|
if (!tempPrintServices.equals(mInstalledServices)) {
|
|
mInstalledServices.clear();
|
|
mInstalledServices.addAll(tempPrintServices);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private boolean readEnabledPrintServicesLocked() {
|
|
Set<ComponentName> tempEnabledServiceNameSet = new HashSet<ComponentName>();
|
|
|
|
String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
|
|
Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
|
|
if (!TextUtils.isEmpty(settingValue)) {
|
|
TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
|
|
splitter.setString(settingValue);
|
|
while (splitter.hasNext()) {
|
|
String string = splitter.next();
|
|
if (TextUtils.isEmpty(string)) {
|
|
continue;
|
|
}
|
|
ComponentName componentName = ComponentName.unflattenFromString(string);
|
|
if (componentName != null) {
|
|
tempEnabledServiceNameSet.add(componentName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!tempEnabledServiceNameSet.equals(mEnabledServices)) {
|
|
mEnabledServices.clear();
|
|
mEnabledServices.addAll(tempEnabledServiceNameSet);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void onConfigurationChangedLocked() {
|
|
final int installedCount = mInstalledServices.size();
|
|
for (int i = 0; i < installedCount; i++) {
|
|
ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo();
|
|
ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName,
|
|
resolveInfo.serviceInfo.name);
|
|
if (mEnabledServices.contains(serviceName)) {
|
|
if (!mActiveServices.containsKey(serviceName)) {
|
|
mActiveServices.put(serviceName, new RemotePrintService(
|
|
mContext, serviceName, mUserId, mSpooler));
|
|
}
|
|
} else {
|
|
RemotePrintService service = mActiveServices.remove(serviceName);
|
|
if (service != null) {
|
|
service.destroy();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void throwIfDestroyedLocked() {
|
|
if (mDestroyed) {
|
|
throw new IllegalStateException("Cannot interact with a destroyed instance.");
|
|
}
|
|
}
|
|
}
|
|
|