/* * 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 mActiveServices = new HashMap(); private final List mInstalledServices = new ArrayList(); private final Set mEnabledServices = new HashSet(); 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 services; synchronized (mLock) { throwIfDestroyedLocked(); if (mActiveServices.isEmpty()) { return; } services = new ArrayList(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 getActiveServicesLocked() { synchronized(mLock) { throwIfDestroyedLocked(); return mActiveServices; } } public Set 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 tempPrintServices = new HashSet(); List 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 tempEnabledServiceNameSet = new HashSet(); 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."); } } }