PrintManagerService.java revision 44720af55a8fdf991929983dad5d53c02851dd1e
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.print;
18
19import android.Manifest;
20import android.content.BroadcastReceiver;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.PackageManager;
26import android.database.ContentObserver;
27import android.net.Uri;
28import android.os.Binder;
29import android.os.Process;
30import android.os.UserHandle;
31import android.print.IPrintClient;
32import android.print.IPrintDocumentAdapter;
33import android.print.IPrintManager;
34import android.print.IPrinterDiscoveryObserver;
35import android.print.PrintAttributes;
36import android.print.PrintJobInfo;
37import android.print.PrinterId;
38import android.provider.Settings;
39import android.util.SparseArray;
40
41import com.android.internal.content.PackageMonitor;
42import com.android.internal.os.BackgroundThread;
43
44import java.util.Iterator;
45import java.util.List;
46import java.util.Set;
47
48public final class PrintManagerService extends IPrintManager.Stub {
49
50    private static final char COMPONENT_NAME_SEPARATOR = ':';
51
52    private final Object mLock = new Object();
53
54    private final Context mContext;
55
56    private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
57
58    private int mCurrentUserId = UserHandle.USER_OWNER;
59
60    public PrintManagerService(Context context) {
61        mContext = context;
62        registerContentObservers();
63        registerBoradcastReceivers();
64    }
65
66    public void systemRuning() {
67        BackgroundThread.getHandler().post(new Runnable() {
68            @Override
69            public void run() {
70                synchronized (mLock) {
71                    UserState userState = getCurrentUserStateLocked();
72                    userState.updateIfNeededLocked();
73                    userState.getSpoolerLocked().start();
74                }
75            }
76        });
77    }
78
79    @Override
80    public PrintJobInfo print(String printJobName, IPrintClient client,
81            IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId,
82            int userId) {
83        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
84        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
85        final UserState userState;
86        final RemotePrintSpooler spooler;
87        synchronized (mLock) {
88            userState = getOrCreateUserStateLocked(resolvedUserId);
89            spooler = userState.getSpoolerLocked();
90        }
91        final long identity = Binder.clearCallingIdentity();
92        try {
93            return spooler.createPrintJob(printJobName, client, documentAdapter,
94                    attributes, resolvedAppId);
95        } finally {
96            Binder.restoreCallingIdentity(identity);
97        }
98    }
99
100    @Override
101    public List<PrintJobInfo> getPrintJobInfos(int appId, int userId) {
102        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
103        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
104        final UserState userState;
105        final RemotePrintSpooler spooler;
106        synchronized (mLock) {
107            userState = getOrCreateUserStateLocked(resolvedUserId);
108            spooler = userState.getSpoolerLocked();
109        }
110        final long identity = Binder.clearCallingIdentity();
111        try {
112            return spooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY,
113                    resolvedAppId);
114        } finally {
115            Binder.restoreCallingIdentity(identity);
116        }
117    }
118
119    @Override
120    public PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId) {
121        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
122        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
123        final UserState userState;
124        final RemotePrintSpooler spooler;
125        synchronized (mLock) {
126            userState = getOrCreateUserStateLocked(resolvedUserId);
127            spooler = userState.getSpoolerLocked();
128        }
129        final long identity = Binder.clearCallingIdentity();
130        try {
131            return spooler.getPrintJobInfo(printJobId, resolvedAppId);
132        } finally {
133            Binder.restoreCallingIdentity(identity);
134        }
135    }
136
137    @Override
138    public void cancelPrintJob(int printJobId, int appId, int userId) {
139        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
140        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
141        final UserState userState;
142        final RemotePrintSpooler spooler;
143        synchronized (mLock) {
144            userState = getOrCreateUserStateLocked(resolvedUserId);
145            spooler = userState.getSpoolerLocked();
146        }
147        final long identity = Binder.clearCallingIdentity();
148        try {
149            PrintJobInfo printJobInfo = spooler.getPrintJobInfo(printJobId, resolvedAppId);
150            if (printJobInfo == null) {
151                return;
152            }
153            if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
154                ComponentName printServiceName = printJobInfo.getPrinterId().getServiceName();
155                RemotePrintService printService = null;
156                synchronized (mLock) {
157                    printService = userState.getActiveServicesLocked().get(printServiceName);
158                }
159                if (printService == null) {
160                    return;
161                }
162                printService.onRequestCancelPrintJob(printJobInfo);
163            } else {
164                // If the print job is failed we do not need cooperation
165                // from the print service.
166                spooler.setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED, null);
167            }
168        } finally {
169            Binder.restoreCallingIdentity(identity);
170        }
171    }
172
173    @Override
174    public void restartPrintJob(int printJobId, int appId, int userId) {
175        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
176        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
177        final RemotePrintSpooler spooler;
178        synchronized (mLock) {
179            spooler = getOrCreateUserStateLocked(resolvedUserId).getSpoolerLocked();
180        }
181        final long identity = Binder.clearCallingIdentity();
182        try {
183            PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
184            if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
185                return;
186            }
187            spooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null);
188        } finally {
189            Binder.restoreCallingIdentity(identity);
190        }
191    }
192
193    @Override
194    public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
195            int userId) {
196        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
197        final UserState userState;
198        synchronized (mLock) {
199            userState = getOrCreateUserStateLocked(resolvedUserId);
200        }
201        final long identity = Binder.clearCallingIdentity();
202        try {
203            userState.createPrinterDiscoverySession(observer);
204        } finally {
205            Binder.restoreCallingIdentity(identity);
206        }
207    }
208
209    @Override
210    public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
211            int userId) {
212        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
213        final UserState userState;
214        synchronized (mLock) {
215            userState = getOrCreateUserStateLocked(resolvedUserId);
216        }
217        final long identity = Binder.clearCallingIdentity();
218        try {
219            userState.destroyPrinterDiscoverySession(observer);
220        } finally {
221            Binder.restoreCallingIdentity(identity);
222        }
223    }
224
225    @Override
226    public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
227            List<PrinterId> priorityList, int userId) {
228        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
229        final UserState userState;
230        synchronized (mLock) {
231            userState = getOrCreateUserStateLocked(resolvedUserId);
232        }
233        final long identity = Binder.clearCallingIdentity();
234        try {
235            userState.startPrinterDiscovery(observer, priorityList);
236        } finally {
237            Binder.restoreCallingIdentity(identity);
238        }
239    }
240
241    @Override
242    public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) {
243        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
244        final UserState userState;
245        synchronized (mLock) {
246            userState = getOrCreateUserStateLocked(resolvedUserId);
247        }
248        final long identity = Binder.clearCallingIdentity();
249        try {
250            userState.stopPrinterDiscovery(observer);
251        } finally {
252            Binder.restoreCallingIdentity(identity);
253        }
254    }
255
256    @Override
257    public void requestPrinterUpdate(PrinterId printerId, int userId) {
258        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
259        final UserState userState;
260        synchronized (mLock) {
261            userState = getOrCreateUserStateLocked(resolvedUserId);
262        }
263        final long identity = Binder.clearCallingIdentity();
264        try {
265            userState.requestPrinterUpdate(printerId);
266        } finally {
267            Binder.restoreCallingIdentity(identity);
268        }
269    }
270
271    private void registerContentObservers() {
272        final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
273                Settings.Secure.ENABLED_PRINT_SERVICES);
274
275        ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
276            @Override
277            public void onChange(boolean selfChange, Uri uri) {
278                if (enabledPrintServicesUri.equals(uri)) {
279                    synchronized (mLock) {
280                        UserState userState = getCurrentUserStateLocked();
281                        userState.updateIfNeededLocked();
282                    }
283                }
284            }
285        };
286
287        mContext.getContentResolver().registerContentObserver(enabledPrintServicesUri,
288                false, observer, UserHandle.USER_ALL);
289    }
290
291    private void registerBoradcastReceivers() {
292        PackageMonitor monitor = new PackageMonitor() {
293            @Override
294            public boolean onPackageChanged(String packageName, int uid, String[] components) {
295                synchronized (mLock) {
296                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
297                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
298                    while (iterator.hasNext()) {
299                        ComponentName componentName = iterator.next();
300                        if (packageName.equals(componentName.getPackageName())) {
301                            userState.updateIfNeededLocked();
302                            return true;
303                        }
304                    }
305                }
306                return false;
307            }
308
309            @Override
310            public void onPackageRemoved(String packageName, int uid) {
311                synchronized (mLock) {
312                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
313                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
314                    while (iterator.hasNext()) {
315                        ComponentName componentName = iterator.next();
316                        if (packageName.equals(componentName.getPackageName())) {
317                            iterator.remove();
318                            persistComponentNamesToSettingLocked(
319                                    Settings.Secure.ENABLED_PRINT_SERVICES,
320                                    userState.getEnabledServices(), getChangingUserId());
321                            userState.updateIfNeededLocked();
322                            return;
323                        }
324                    }
325                }
326            }
327
328            @Override
329            public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
330                    int uid, boolean doit) {
331                synchronized (mLock) {
332                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
333                    boolean stoppedSomePackages = false;
334                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
335                    while (iterator.hasNext()) {
336                        ComponentName componentName = iterator.next();
337                        String componentPackage = componentName.getPackageName();
338                        for (String stoppedPackage : stoppedPackages) {
339                            if (componentPackage.equals(stoppedPackage)) {
340                                if (!doit) {
341                                    return true;
342                                }
343                                stoppedSomePackages = true;
344                                break;
345                            }
346                        }
347                    }
348                    if (stoppedSomePackages) {
349                        userState.updateIfNeededLocked();
350                    }
351                    return false;
352                }
353            }
354
355            private void persistComponentNamesToSettingLocked(String settingName,
356                    Set<ComponentName> componentNames, int userId) {
357                StringBuilder builder = new StringBuilder();
358                for (ComponentName componentName : componentNames) {
359                    if (builder.length() > 0) {
360                        builder.append(COMPONENT_NAME_SEPARATOR);
361                    }
362                    builder.append(componentName.flattenToShortString());
363                }
364                Settings.Secure.putStringForUser(mContext.getContentResolver(),
365                        settingName, builder.toString(), userId);
366            }
367        };
368
369        // package changes
370        monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
371                UserHandle.ALL, true);
372
373        // user changes
374        IntentFilter intentFilter = new IntentFilter();
375        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
376
377        mContext.registerReceiverAsUser(new BroadcastReceiver() {
378            @Override
379            public void onReceive(Context context, Intent intent) {
380                String action = intent.getAction();
381                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
382                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
383                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
384                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
385                }
386            }
387        }, UserHandle.ALL, intentFilter, null, BackgroundThread.getHandler());
388    }
389
390    private UserState getCurrentUserStateLocked() {
391        return getOrCreateUserStateLocked(mCurrentUserId);
392    }
393
394    private UserState getOrCreateUserStateLocked(int userId) {
395        UserState userState = mUserStates.get(userId);
396        if (userState == null) {
397            userState = new UserState(mContext, userId, mLock);
398            mUserStates.put(userId, userState);
399        }
400        return userState;
401    }
402
403    private void switchUser(int newUserId) {
404        synchronized (mLock) {
405            if (newUserId == mCurrentUserId) {
406                return;
407            }
408            mCurrentUserId = newUserId;
409            UserState userState = getCurrentUserStateLocked();
410            userState.updateIfNeededLocked();
411            userState.getSpoolerLocked().start();
412        }
413    }
414
415    private void removeUser(int removedUserId) {
416        synchronized (mLock) {
417            UserState userState = mUserStates.get(removedUserId);
418            if (userState != null) {
419                userState.destroyLocked();
420                mUserStates.remove(removedUserId);
421            }
422        }
423    }
424
425    private int resolveCallingAppEnforcingPermissions(int appId) {
426        final int callingUid = Binder.getCallingUid();
427        if (callingUid == 0 || callingUid == Process.SYSTEM_UID
428                || callingUid == Process.SHELL_UID) {
429            return appId;
430        }
431        final int callingAppId = UserHandle.getAppId(callingUid);
432        if (appId == callingAppId) {
433            return appId;
434        }
435        if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
436                != PackageManager.PERMISSION_GRANTED) {
437            throw new SecurityException("Call from app " + callingAppId + " as app "
438                    + appId + " without permission ACCESS_ALL_PRINT_JOBS");
439        }
440        return appId;
441    }
442
443    private int resolveCallingUserEnforcingPermissions(int userId) {
444        final int callingUid = Binder.getCallingUid();
445        if (callingUid == 0 || callingUid == Process.SYSTEM_UID
446                || callingUid == Process.SHELL_UID) {
447            return userId;
448        }
449        final int callingUserId = UserHandle.getUserId(callingUid);
450        if (callingUserId == userId) {
451            return userId;
452        }
453        if (mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
454                != PackageManager.PERMISSION_GRANTED
455            ||  mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS)
456                != PackageManager.PERMISSION_GRANTED) {
457            if (userId == UserHandle.USER_CURRENT_OR_SELF) {
458                return callingUserId;
459            }
460            throw new SecurityException("Call from user " + callingUserId + " as user "
461                + userId + " without permission INTERACT_ACROSS_USERS or "
462                + "INTERACT_ACROSS_USERS_FULL not allowed.");
463        }
464        if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
465            return mCurrentUserId;
466        }
467        throw new IllegalArgumentException("Calling user can be changed to only "
468                + "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
469    }
470}
471