PrintManagerService.java revision 7bfbbcb04bf4ba8f3069b2df136f708c9849bacf
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.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.pm.PackageManager;
29import android.content.pm.ResolveInfo;
30import android.content.pm.ServiceInfo;
31import android.database.ContentObserver;
32import android.net.Uri;
33import android.os.Binder;
34import android.os.Bundle;
35import android.os.Process;
36import android.os.RemoteException;
37import android.os.UserHandle;
38import android.print.IPrintDocumentAdapter;
39import android.print.IPrintJobStateChangeListener;
40import android.print.IPrintManager;
41import android.print.IPrinterDiscoveryObserver;
42import android.print.PrintAttributes;
43import android.print.PrintJobId;
44import android.print.PrintJobInfo;
45import android.print.PrinterId;
46import android.printservice.PrintServiceInfo;
47import android.provider.Settings;
48import android.text.TextUtils;
49import android.util.SparseArray;
50
51import com.android.internal.R;
52import com.android.internal.content.PackageMonitor;
53import com.android.internal.os.BackgroundThread;
54
55import java.io.FileDescriptor;
56import java.io.PrintWriter;
57import java.util.Iterator;
58import java.util.List;
59import java.util.Set;
60
61public final class PrintManagerService extends IPrintManager.Stub {
62
63    private static final char COMPONENT_NAME_SEPARATOR = ':';
64
65    private static final String EXTRA_PRINT_SERVICE_COMPONENT_NAME =
66            "EXTRA_PRINT_SERVICE_COMPONENT_NAME";
67
68    private final Object mLock = new Object();
69
70    private final Context mContext;
71
72    private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
73
74    private int mCurrentUserId = UserHandle.USER_OWNER;
75
76    public PrintManagerService(Context context) {
77        mContext = context;
78        registerContentObservers();
79        registerBoradcastReceivers();
80    }
81
82    public void systemRuning() {
83        BackgroundThread.getHandler().post(new Runnable() {
84            @Override
85            public void run() {
86                final UserState userState;
87                synchronized (mLock) {
88                    userState = getCurrentUserStateLocked();
89                    userState.updateIfNeededLocked();
90                }
91                // This is the first time we switch to this user after boot, so
92                // now is the time to remove obsolete print jobs since they
93                // are from the last boot and no application would query them.
94                userState.removeObsoletePrintJobs();
95            }
96        });
97    }
98
99    @Override
100    public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
101            PrintAttributes attributes, String packageName, int appId, int userId) {
102        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
103        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
104        String resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName);
105        final UserState userState;
106        synchronized (mLock) {
107            userState = getOrCreateUserStateLocked(resolvedUserId);
108        }
109        final long identity = Binder.clearCallingIdentity();
110        try {
111            return userState.print(printJobName, adapter, attributes,
112                    resolvedPackageName, resolvedAppId);
113        } finally {
114            Binder.restoreCallingIdentity(identity);
115        }
116    }
117
118    @Override
119    public List<PrintJobInfo> getPrintJobInfos(int appId, int userId) {
120        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
121        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
122        final UserState userState;
123        synchronized (mLock) {
124            userState = getOrCreateUserStateLocked(resolvedUserId);
125        }
126        final long identity = Binder.clearCallingIdentity();
127        try {
128            return userState.getPrintJobInfos(resolvedAppId);
129        } finally {
130            Binder.restoreCallingIdentity(identity);
131        }
132    }
133
134    @Override
135    public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId, int userId) {
136        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
137        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
138        final UserState userState;
139        synchronized (mLock) {
140            userState = getOrCreateUserStateLocked(resolvedUserId);
141        }
142        final long identity = Binder.clearCallingIdentity();
143        try {
144            return userState.getPrintJobInfo(printJobId, resolvedAppId);
145        } finally {
146            Binder.restoreCallingIdentity(identity);
147        }
148    }
149
150    @Override
151    public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) {
152        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
153        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
154        final UserState userState;
155        synchronized (mLock) {
156            userState = getOrCreateUserStateLocked(resolvedUserId);
157        }
158        final long identity = Binder.clearCallingIdentity();
159        try {
160            userState.cancelPrintJob(printJobId, resolvedAppId);
161        } finally {
162            Binder.restoreCallingIdentity(identity);
163        }
164    }
165
166    @Override
167    public void restartPrintJob(PrintJobId printJobId, int appId, int userId) {
168        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
169        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
170        final UserState userState;
171        synchronized (mLock) {
172            userState = getOrCreateUserStateLocked(resolvedUserId);
173        }
174        final long identity = Binder.clearCallingIdentity();
175        try {
176            userState.restartPrintJob(printJobId, resolvedAppId);
177        } finally {
178            Binder.restoreCallingIdentity(identity);
179        }
180    }
181
182    @Override
183    public List<PrintServiceInfo> getEnabledPrintServices(int userId) {
184        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
185        final UserState userState;
186        synchronized (mLock) {
187            userState = getOrCreateUserStateLocked(resolvedUserId);
188        }
189        final long identity = Binder.clearCallingIdentity();
190        try {
191            return userState.getEnabledPrintServices();
192        } finally {
193            Binder.restoreCallingIdentity(identity);
194        }
195    }
196
197    @Override
198    public List<PrintServiceInfo> getInstalledPrintServices(int userId) {
199        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
200        final UserState userState;
201        synchronized (mLock) {
202            userState = getOrCreateUserStateLocked(resolvedUserId);
203        }
204        final long identity = Binder.clearCallingIdentity();
205        try {
206            return userState.getInstalledPrintServices();
207        } finally {
208            Binder.restoreCallingIdentity(identity);
209        }
210    }
211
212    @Override
213    public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
214            int userId) {
215        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
216        final UserState userState;
217        synchronized (mLock) {
218            userState = getOrCreateUserStateLocked(resolvedUserId);
219        }
220        final long identity = Binder.clearCallingIdentity();
221        try {
222            userState.createPrinterDiscoverySession(observer);
223        } finally {
224            Binder.restoreCallingIdentity(identity);
225        }
226    }
227
228    @Override
229    public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
230            int userId) {
231        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
232        final UserState userState;
233        synchronized (mLock) {
234            userState = getOrCreateUserStateLocked(resolvedUserId);
235        }
236        final long identity = Binder.clearCallingIdentity();
237        try {
238            userState.destroyPrinterDiscoverySession(observer);
239        } finally {
240            Binder.restoreCallingIdentity(identity);
241        }
242    }
243
244    @Override
245    public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
246            List<PrinterId> priorityList, int userId) {
247        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
248        final UserState userState;
249        synchronized (mLock) {
250            userState = getOrCreateUserStateLocked(resolvedUserId);
251        }
252        final long identity = Binder.clearCallingIdentity();
253        try {
254            userState.startPrinterDiscovery(observer, priorityList);
255        } finally {
256            Binder.restoreCallingIdentity(identity);
257        }
258    }
259
260    @Override
261    public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) {
262        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
263        final UserState userState;
264        synchronized (mLock) {
265            userState = getOrCreateUserStateLocked(resolvedUserId);
266        }
267        final long identity = Binder.clearCallingIdentity();
268        try {
269            userState.stopPrinterDiscovery(observer);
270        } finally {
271            Binder.restoreCallingIdentity(identity);
272        }
273    }
274
275    @Override
276    public void validatePrinters(List<PrinterId> printerIds, int userId) {
277        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
278        final UserState userState;
279        synchronized (mLock) {
280            userState = getOrCreateUserStateLocked(resolvedUserId);
281        }
282        final long identity = Binder.clearCallingIdentity();
283        try {
284            userState.validatePrinters(printerIds);
285        } finally {
286            Binder.restoreCallingIdentity(identity);
287        }
288    }
289
290    @Override
291    public void startPrinterStateTracking(PrinterId printerId, int userId) {
292        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
293        final UserState userState;
294        synchronized (mLock) {
295            userState = getOrCreateUserStateLocked(resolvedUserId);
296        }
297        final long identity = Binder.clearCallingIdentity();
298        try {
299            userState.startPrinterStateTracking(printerId);
300        } finally {
301            Binder.restoreCallingIdentity(identity);
302        }
303    }
304
305    @Override
306    public void stopPrinterStateTracking(PrinterId printerId, int userId) {
307        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
308        final UserState userState;
309        synchronized (mLock) {
310            userState = getOrCreateUserStateLocked(resolvedUserId);
311        }
312        final long identity = Binder.clearCallingIdentity();
313        try {
314            userState.stopPrinterStateTracking(printerId);
315        } finally {
316            Binder.restoreCallingIdentity(identity);
317        }
318    }
319
320    @Override
321    public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
322            int appId, int userId) throws RemoteException {
323        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
324        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
325        final UserState userState;
326        synchronized (mLock) {
327            userState = getOrCreateUserStateLocked(resolvedUserId);
328        }
329        final long identity = Binder.clearCallingIdentity();
330        try {
331            userState.addPrintJobStateChangeListener(listener, resolvedAppId);
332        } finally {
333            Binder.restoreCallingIdentity(identity);
334        }
335    }
336
337    @Override
338    public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener,
339            int userId) {
340        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
341        final UserState userState;
342        synchronized (mLock) {
343            userState = getOrCreateUserStateLocked(resolvedUserId);
344        }
345        final long identity = Binder.clearCallingIdentity();
346        try {
347            userState.removePrintJobStateChangeListener(listener);
348        } finally {
349            Binder.restoreCallingIdentity(identity);
350        }
351    }
352
353    @Override
354    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
355        if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
356                != PackageManager.PERMISSION_GRANTED) {
357            pw.println("Permission Denial: can't dump PrintManager from from pid="
358                    + Binder.getCallingPid()
359                    + ", uid=" + Binder.getCallingUid());
360            return;
361        }
362
363        synchronized (mLock) {
364            pw.println("PRINT MANAGER STATE (dumpsys print)");
365            final int userStateCount = mUserStates.size();
366            for (int i = 0; i < userStateCount; i++) {
367                UserState userState = mUserStates.get(i);
368                userState.dump(fd, pw, "");
369                pw.println();
370            }
371        }
372    }
373
374    private void registerContentObservers() {
375        final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
376                Settings.Secure.ENABLED_PRINT_SERVICES);
377
378        ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
379            @Override
380            public void onChange(boolean selfChange, Uri uri) {
381                if (enabledPrintServicesUri.equals(uri)) {
382                    synchronized (mLock) {
383                        UserState userState = getCurrentUserStateLocked();
384                        userState.updateIfNeededLocked();
385                    }
386                }
387            }
388        };
389
390        mContext.getContentResolver().registerContentObserver(enabledPrintServicesUri,
391                false, observer, UserHandle.USER_ALL);
392    }
393
394    private void registerBoradcastReceivers() {
395        PackageMonitor monitor = new PackageMonitor() {
396            @Override
397            public boolean onPackageChanged(String packageName, int uid, String[] components) {
398                synchronized (mLock) {
399                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
400                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
401                    while (iterator.hasNext()) {
402                        ComponentName componentName = iterator.next();
403                        if (packageName.equals(componentName.getPackageName())) {
404                            userState.updateIfNeededLocked();
405                            return true;
406                        }
407                    }
408                }
409                return false;
410            }
411
412            @Override
413            public void onPackageRemoved(String packageName, int uid) {
414                synchronized (mLock) {
415                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
416                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
417                    while (iterator.hasNext()) {
418                        ComponentName componentName = iterator.next();
419                        if (packageName.equals(componentName.getPackageName())) {
420                            iterator.remove();
421                            persistComponentNamesToSettingLocked(
422                                    Settings.Secure.ENABLED_PRINT_SERVICES,
423                                    userState.getEnabledServices(), getChangingUserId());
424                            userState.updateIfNeededLocked();
425                            return;
426                        }
427                    }
428                }
429            }
430
431            @Override
432            public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
433                    int uid, boolean doit) {
434                synchronized (mLock) {
435                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
436                    boolean stoppedSomePackages = false;
437                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
438                    while (iterator.hasNext()) {
439                        ComponentName componentName = iterator.next();
440                        String componentPackage = componentName.getPackageName();
441                        for (String stoppedPackage : stoppedPackages) {
442                            if (componentPackage.equals(stoppedPackage)) {
443                                if (!doit) {
444                                    return true;
445                                }
446                                stoppedSomePackages = true;
447                                break;
448                            }
449                        }
450                    }
451                    if (stoppedSomePackages) {
452                        userState.updateIfNeededLocked();
453                    }
454                    return false;
455                }
456            }
457
458            @Override
459            public void onPackageAdded(String packageName, int uid) {
460                Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
461                intent.setPackage(packageName);
462
463                List<ResolveInfo> installedServices = mContext.getPackageManager()
464                        .queryIntentServicesAsUser(intent, PackageManager.GET_SERVICES,
465                                getChangingUserId());
466
467                if (installedServices == null) {
468                    return;
469                }
470
471                final int installedServiceCount = installedServices.size();
472                for (int i = 0; i < installedServiceCount; i++) {
473                    ServiceInfo serviceInfo = installedServices.get(i).serviceInfo;
474                    ComponentName component = new ComponentName(serviceInfo.packageName,
475                            serviceInfo.name);
476                    String label = serviceInfo.loadLabel(mContext.getPackageManager()).toString();
477                    showEnableInstalledPrintServiceNotification(component, label);
478                }
479            }
480
481            private void persistComponentNamesToSettingLocked(String settingName,
482                    Set<ComponentName> componentNames, int userId) {
483                StringBuilder builder = new StringBuilder();
484                for (ComponentName componentName : componentNames) {
485                    if (builder.length() > 0) {
486                        builder.append(COMPONENT_NAME_SEPARATOR);
487                    }
488                    builder.append(componentName.flattenToShortString());
489                }
490                Settings.Secure.putStringForUser(mContext.getContentResolver(),
491                        settingName, builder.toString(), userId);
492            }
493        };
494
495        // package changes
496        monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
497                UserHandle.ALL, true);
498
499        // user changes
500        IntentFilter intentFilter = new IntentFilter();
501        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
502        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
503
504        mContext.registerReceiverAsUser(new BroadcastReceiver() {
505            @Override
506            public void onReceive(Context context, Intent intent) {
507                String action = intent.getAction();
508                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
509                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
510                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
511                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
512                }
513            }
514        }, UserHandle.ALL, intentFilter, null, BackgroundThread.getHandler());
515    }
516
517    private UserState getCurrentUserStateLocked() {
518        return getOrCreateUserStateLocked(mCurrentUserId);
519    }
520
521    private UserState getOrCreateUserStateLocked(int userId) {
522        UserState userState = mUserStates.get(userId);
523        if (userState == null) {
524            userState = new UserState(mContext, userId, mLock);
525            mUserStates.put(userId, userState);
526        }
527        return userState;
528    }
529
530    private void switchUser(int newUserId) {
531        UserState userState;
532        synchronized (mLock) {
533            if (newUserId == mCurrentUserId) {
534                return;
535            }
536            mCurrentUserId = newUserId;
537            userState = mUserStates.get(mCurrentUserId);
538            if (userState == null) {
539                userState = getCurrentUserStateLocked();
540                userState.updateIfNeededLocked();
541            } else {
542                userState.updateIfNeededLocked();
543            }
544        }
545        // This is the first time we switch to this user after boot, so
546        // now is the time to remove obsolete print jobs since they
547        // are from the last boot and no application would query them.
548        userState.removeObsoletePrintJobs();
549    }
550
551    private void removeUser(int removedUserId) {
552        synchronized (mLock) {
553            UserState userState = mUserStates.get(removedUserId);
554            if (userState != null) {
555                userState.destroyLocked();
556                mUserStates.remove(removedUserId);
557            }
558        }
559    }
560
561    private int resolveCallingAppEnforcingPermissions(int appId) {
562        final int callingUid = Binder.getCallingUid();
563        if (callingUid == 0 || callingUid == Process.SYSTEM_UID
564                || callingUid == Process.SHELL_UID) {
565            return appId;
566        }
567        final int callingAppId = UserHandle.getAppId(callingUid);
568        if (appId == callingAppId) {
569            return appId;
570        }
571        if (mContext.checkCallingPermission(
572                "com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS")
573                != PackageManager.PERMISSION_GRANTED) {
574            throw new SecurityException("Call from app " + callingAppId + " as app "
575                    + appId + " without com.android.printspooler.permission"
576                    + ".ACCESS_ALL_PRINT_JOBS");
577        }
578        return appId;
579    }
580
581    private int resolveCallingUserEnforcingPermissions(int userId) {
582        final int callingUid = Binder.getCallingUid();
583        if (callingUid == 0 || callingUid == Process.SYSTEM_UID
584                || callingUid == Process.SHELL_UID) {
585            return userId;
586        }
587        final int callingUserId = UserHandle.getUserId(callingUid);
588        if (callingUserId == userId) {
589            return userId;
590        }
591        if (mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
592                != PackageManager.PERMISSION_GRANTED
593            ||  mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS)
594                != PackageManager.PERMISSION_GRANTED) {
595            if (userId == UserHandle.USER_CURRENT_OR_SELF) {
596                return callingUserId;
597            }
598            throw new SecurityException("Call from user " + callingUserId + " as user "
599                + userId + " without permission INTERACT_ACROSS_USERS or "
600                + "INTERACT_ACROSS_USERS_FULL not allowed.");
601        }
602        if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
603            return mCurrentUserId;
604        }
605        throw new IllegalArgumentException("Calling user can be changed to only "
606                + "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
607    }
608
609    private String resolveCallingPackageNameEnforcingSecurity(String packageName) {
610        if (TextUtils.isEmpty(packageName)) {
611            return null;
612        }
613        String[] packages = mContext.getPackageManager().getPackagesForUid(
614                Binder.getCallingUid());
615        final int packageCount = packages.length;
616        for (int i = 0; i < packageCount; i++) {
617            if (packageName.equals(packages[i])) {
618                return packageName;
619            }
620        }
621        return null;
622    }
623
624    private void showEnableInstalledPrintServiceNotification(ComponentName component,
625            String label) {
626        Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
627        intent.putExtra(EXTRA_PRINT_SERVICE_COMPONENT_NAME, component.flattenToString());
628
629        PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent,
630                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT, null);
631
632        Notification.Builder builder = new Notification.Builder(mContext)
633                .setSmallIcon(R.drawable.ic_print)
634                .setContentTitle(mContext.getString(R.string.print_service_installed_title, label))
635                .setContentText(mContext.getString(R.string.print_service_installed_message))
636                .setContentIntent(pendingIntent)
637                .setWhen(System.currentTimeMillis())
638                .setAutoCancel(true)
639                .setShowWhen(true);
640
641        NotificationManager notificationManager = (NotificationManager) mContext
642                .getSystemService(Context.NOTIFICATION_SERVICE);
643
644        String notificationTag = getClass().getName() + ":" + component.flattenToString();
645        notificationManager.notify(notificationTag, 0, builder.build());
646    }
647}
648