blob: 86e76852b4d5d042c1c4e8b36ee9d13d7086f318 [file] [log] [blame]
/*
* 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.Manifest;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
import android.print.IPrintClient;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintManager;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
import android.provider.Settings;
import android.util.SparseArray;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public final class PrintManagerService extends IPrintManager.Stub {
private static final char COMPONENT_NAME_SEPARATOR = ':';
private final Object mLock = new Object();
private final Context mContext;
private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
private int mCurrentUserId = UserHandle.USER_OWNER;
public PrintManagerService(Context context) {
mContext = context;
registerContentObservers();
registerBoradcastReceivers();
}
public void systemRuning() {
BackgroundThread.getHandler().post(new Runnable() {
@Override
public void run() {
synchronized (mLock) {
UserState userState = getCurrentUserStateLocked();
userState.updateIfNeededLocked();
userState.getSpoolerLocked().notifyClientForActivteJobs();
}
}
});
}
@Override
public PrintJobInfo print(String printJobName, IPrintClient client,
IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId,
int userId) {
final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
final RemotePrintSpooler spooler;
synchronized (mLock) {
userState = getOrCreateUserStateLocked(resolvedUserId);
spooler = userState.getSpoolerLocked();
}
final long identity = Binder.clearCallingIdentity();
try {
return spooler.createPrintJob(printJobName, client, documentAdapter,
attributes, resolvedAppId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public List<PrintJobInfo> getPrintJobInfos(int appId, int userId) {
final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
final RemotePrintSpooler spooler;
synchronized (mLock) {
userState = getOrCreateUserStateLocked(resolvedUserId);
spooler = userState.getSpoolerLocked();
}
final long identity = Binder.clearCallingIdentity();
try {
return spooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY,
resolvedAppId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId) {
final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
final RemotePrintSpooler spooler;
synchronized (mLock) {
userState = getOrCreateUserStateLocked(resolvedUserId);
spooler = userState.getSpoolerLocked();
}
final long identity = Binder.clearCallingIdentity();
try {
return spooler.getPrintJobInfo(printJobId, resolvedAppId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void cancelPrintJob(int printJobId, int appId, int userId) {
final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
final RemotePrintSpooler spooler;
synchronized (mLock) {
userState = getOrCreateUserStateLocked(resolvedUserId);
spooler = userState.getSpoolerLocked();
}
final long identity = Binder.clearCallingIdentity();
try {
if (spooler.cancelPrintJob(printJobId, resolvedAppId)) {
return;
}
PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
if (printJobInfo == null) {
return;
}
ComponentName printServiceName = printJobInfo.getPrinterId().getService();
RemotePrintService printService = null;
synchronized (mLock) {
printService = userState.getActiveServices().get(printServiceName);
}
if (printService == null) {
return;
}
printService.onRequestCancelPrintJob(printJobInfo);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private void registerContentObservers() {
final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
Settings.Secure.ENABLED_PRINT_SERVICES);
ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (enabledPrintServicesUri.equals(uri)) {
synchronized (mLock) {
UserState userState = getCurrentUserStateLocked();
userState.updateIfNeededLocked();
}
}
}
};
mContext.getContentResolver().registerContentObserver(enabledPrintServicesUri,
false, observer, UserHandle.USER_ALL);
}
private void registerBoradcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
@Override
public boolean onPackageChanged(String packageName, int uid, String[] components) {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(getChangingUserId());
Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
while (iterator.hasNext()) {
ComponentName componentName = iterator.next();
if (packageName.equals(componentName.getPackageName())) {
userState.updateIfNeededLocked();
return true;
}
}
}
return false;
}
@Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(getChangingUserId());
Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
while (iterator.hasNext()) {
ComponentName componentName = iterator.next();
if (packageName.equals(componentName.getPackageName())) {
iterator.remove();
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_PRINT_SERVICES,
userState.getEnabledServices(), getChangingUserId());
userState.updateIfNeededLocked();
return;
}
}
}
}
@Override
public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
int uid, boolean doit) {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(getChangingUserId());
boolean stoppedSomePackages = false;
Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
while (iterator.hasNext()) {
ComponentName componentName = iterator.next();
String componentPackage = componentName.getPackageName();
for (String stoppedPackage : stoppedPackages) {
if (componentPackage.equals(stoppedPackage)) {
if (!doit) {
return true;
}
stoppedSomePackages = true;
break;
}
}
}
if (stoppedSomePackages) {
userState.updateIfNeededLocked();
}
return false;
}
}
private void persistComponentNamesToSettingLocked(String settingName,
Set<ComponentName> componentNames, int userId) {
StringBuilder builder = new StringBuilder();
for (ComponentName componentName : componentNames) {
if (builder.length() > 0) {
builder.append(COMPONENT_NAME_SEPARATOR);
}
builder.append(componentName.flattenToShortString());
}
Settings.Secure.putStringForUser(mContext.getContentResolver(),
settingName, builder.toString(), userId);
}
};
// package changes
monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
UserHandle.ALL, true);
// user changes
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
}
}
}, UserHandle.ALL, intentFilter, null, BackgroundThread.getHandler());
}
private UserState getCurrentUserStateLocked() {
return getOrCreateUserStateLocked(mCurrentUserId);
}
private UserState getOrCreateUserStateLocked(int userId) {
UserState userState = mUserStates.get(userId);
if (userState == null) {
userState = new UserState(mContext, userId, mLock);
mUserStates.put(userId, userState);
}
return userState;
}
private void switchUser(int newUserId) {
synchronized (mLock) {
if (newUserId == mCurrentUserId) {
return;
}
mCurrentUserId = newUserId;
UserState userState = getCurrentUserStateLocked();
userState.updateIfNeededLocked();
userState.getSpoolerLocked().notifyClientForActivteJobs();
}
}
private void removeUser(int removedUserId) {
synchronized (mLock) {
UserState userState = mUserStates.get(removedUserId);
if (userState != null) {
userState.destroyLocked();
mUserStates.remove(removedUserId);
}
}
}
private int resolveCallingAppEnforcingPermissions(int appId) {
final int callingUid = Binder.getCallingUid();
if (callingUid == 0 || callingUid == Process.SYSTEM_UID
|| callingUid == Process.SHELL_UID) {
return appId;
}
final int callingAppId = UserHandle.getAppId(callingUid);
if (appId == callingAppId) {
return appId;
}
if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Call from app " + callingAppId + " as app "
+ appId + " without permission ACCESS_ALL_PRINT_JOBS");
}
return appId;
}
private int resolveCallingUserEnforcingPermissions(int userId) {
final int callingUid = Binder.getCallingUid();
if (callingUid == 0 || callingUid == Process.SYSTEM_UID
|| callingUid == Process.SHELL_UID) {
return userId;
}
final int callingUserId = UserHandle.getUserId(callingUid);
if (callingUserId == userId) {
return userId;
}
if (mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED
|| mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS)
!= PackageManager.PERMISSION_GRANTED) {
if (userId == UserHandle.USER_CURRENT_OR_SELF) {
return callingUserId;
}
throw new SecurityException("Call from user " + callingUserId + " as user "
+ userId + " without permission INTERACT_ACROSS_USERS or "
+ "INTERACT_ACROSS_USERS_FULL not allowed.");
}
if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
return mCurrentUserId;
}
throw new IllegalArgumentException("Calling user can be changed to only "
+ "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
}
}