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            final long identity = Binder.clearCallingIdentity();
365            try {
366                pw.println("PRINT MANAGER STATE (dumpsys print)");
367                final int userStateCount = mUserStates.size();
368                for (int i = 0; i < userStateCount; i++) {
369                    UserState userState = mUserStates.valueAt(i);
370                    userState.dump(fd, pw, "");
371                    pw.println();
372                }
373            } finally {
374                Binder.restoreCallingIdentity(identity);
375            }
376        }
377    }
378
379    private void registerContentObservers() {
380        final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
381                Settings.Secure.ENABLED_PRINT_SERVICES);
382
383        ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
384            @Override
385            public void onChange(boolean selfChange, Uri uri) {
386                if (enabledPrintServicesUri.equals(uri)) {
387                    synchronized (mLock) {
388                        UserState userState = getCurrentUserStateLocked();
389                        userState.updateIfNeededLocked();
390                    }
391                }
392            }
393        };
394
395        mContext.getContentResolver().registerContentObserver(enabledPrintServicesUri,
396                false, observer, UserHandle.USER_ALL);
397    }
398
399    private void registerBoradcastReceivers() {
400        PackageMonitor monitor = new PackageMonitor() {
401            @Override
402            public boolean onPackageChanged(String packageName, int uid, String[] components) {
403                synchronized (mLock) {
404                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
405                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
406                    while (iterator.hasNext()) {
407                        ComponentName componentName = iterator.next();
408                        if (packageName.equals(componentName.getPackageName())) {
409                            userState.updateIfNeededLocked();
410                            return true;
411                        }
412                    }
413                }
414                return false;
415            }
416
417            @Override
418            public void onPackageRemoved(String packageName, int uid) {
419                synchronized (mLock) {
420                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
421                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
422                    while (iterator.hasNext()) {
423                        ComponentName componentName = iterator.next();
424                        if (packageName.equals(componentName.getPackageName())) {
425                            iterator.remove();
426                            persistComponentNamesToSettingLocked(
427                                    Settings.Secure.ENABLED_PRINT_SERVICES,
428                                    userState.getEnabledServices(), getChangingUserId());
429                            userState.updateIfNeededLocked();
430                            return;
431                        }
432                    }
433                }
434            }
435
436            @Override
437            public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
438                    int uid, boolean doit) {
439                synchronized (mLock) {
440                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
441                    boolean stoppedSomePackages = false;
442                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
443                    while (iterator.hasNext()) {
444                        ComponentName componentName = iterator.next();
445                        String componentPackage = componentName.getPackageName();
446                        for (String stoppedPackage : stoppedPackages) {
447                            if (componentPackage.equals(stoppedPackage)) {
448                                if (!doit) {
449                                    return true;
450                                }
451                                stoppedSomePackages = true;
452                                break;
453                            }
454                        }
455                    }
456                    if (stoppedSomePackages) {
457                        userState.updateIfNeededLocked();
458                    }
459                    return false;
460                }
461            }
462
463            @Override
464            public void onPackageAdded(String packageName, int uid) {
465                Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
466                intent.setPackage(packageName);
467
468                List<ResolveInfo> installedServices = mContext.getPackageManager()
469                        .queryIntentServicesAsUser(intent, PackageManager.GET_SERVICES,
470                                getChangingUserId());
471
472                if (installedServices == null) {
473                    return;
474                }
475
476                final int installedServiceCount = installedServices.size();
477                for (int i = 0; i < installedServiceCount; i++) {
478                    ServiceInfo serviceInfo = installedServices.get(i).serviceInfo;
479                    ComponentName component = new ComponentName(serviceInfo.packageName,
480                            serviceInfo.name);
481                    String label = serviceInfo.loadLabel(mContext.getPackageManager()).toString();
482                    showEnableInstalledPrintServiceNotification(component, label,
483                            getChangingUserId());
484                }
485            }
486
487            private void persistComponentNamesToSettingLocked(String settingName,
488                    Set<ComponentName> componentNames, int userId) {
489                StringBuilder builder = new StringBuilder();
490                for (ComponentName componentName : componentNames) {
491                    if (builder.length() > 0) {
492                        builder.append(COMPONENT_NAME_SEPARATOR);
493                    }
494                    builder.append(componentName.flattenToShortString());
495                }
496                Settings.Secure.putStringForUser(mContext.getContentResolver(),
497                        settingName, builder.toString(), userId);
498            }
499        };
500
501        // package changes
502        monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
503                UserHandle.ALL, true);
504
505        // user changes
506        IntentFilter intentFilter = new IntentFilter();
507        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
508        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
509
510        mContext.registerReceiverAsUser(new BroadcastReceiver() {
511            @Override
512            public void onReceive(Context context, Intent intent) {
513                String action = intent.getAction();
514                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
515                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
516                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
517                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
518                }
519            }
520        }, UserHandle.ALL, intentFilter, null, BackgroundThread.getHandler());
521    }
522
523    private UserState getCurrentUserStateLocked() {
524        return getOrCreateUserStateLocked(mCurrentUserId);
525    }
526
527    private UserState getOrCreateUserStateLocked(int userId) {
528        UserState userState = mUserStates.get(userId);
529        if (userState == null) {
530            userState = new UserState(mContext, userId, mLock);
531            mUserStates.put(userId, userState);
532        }
533        return userState;
534    }
535
536    private void switchUser(int newUserId) {
537        UserState userState;
538        synchronized (mLock) {
539            if (newUserId == mCurrentUserId) {
540                return;
541            }
542            mCurrentUserId = newUserId;
543            userState = mUserStates.get(mCurrentUserId);
544            if (userState == null) {
545                userState = getCurrentUserStateLocked();
546                userState.updateIfNeededLocked();
547            } else {
548                userState.updateIfNeededLocked();
549            }
550        }
551        // This is the first time we switch to this user after boot, so
552        // now is the time to remove obsolete print jobs since they
553        // are from the last boot and no application would query them.
554        userState.removeObsoletePrintJobs();
555    }
556
557    private void removeUser(int removedUserId) {
558        synchronized (mLock) {
559            UserState userState = mUserStates.get(removedUserId);
560            if (userState != null) {
561                userState.destroyLocked();
562                mUserStates.remove(removedUserId);
563            }
564        }
565    }
566
567    private int resolveCallingAppEnforcingPermissions(int appId) {
568        final int callingUid = Binder.getCallingUid();
569        if (callingUid == 0 || callingUid == Process.SYSTEM_UID
570                || callingUid == Process.SHELL_UID) {
571            return appId;
572        }
573        final int callingAppId = UserHandle.getAppId(callingUid);
574        if (appId == callingAppId) {
575            return appId;
576        }
577        if (mContext.checkCallingPermission(
578                "com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS")
579                != PackageManager.PERMISSION_GRANTED) {
580            throw new SecurityException("Call from app " + callingAppId + " as app "
581                    + appId + " without com.android.printspooler.permission"
582                    + ".ACCESS_ALL_PRINT_JOBS");
583        }
584        return appId;
585    }
586
587    private int resolveCallingUserEnforcingPermissions(int userId) {
588        final int callingUid = Binder.getCallingUid();
589        if (callingUid == 0 || callingUid == Process.SYSTEM_UID
590                || callingUid == Process.SHELL_UID) {
591            return userId;
592        }
593        final int callingUserId = UserHandle.getUserId(callingUid);
594        if (callingUserId == userId) {
595            return userId;
596        }
597        if (mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
598                != PackageManager.PERMISSION_GRANTED
599            ||  mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS)
600                != PackageManager.PERMISSION_GRANTED) {
601            if (userId == UserHandle.USER_CURRENT_OR_SELF) {
602                return callingUserId;
603            }
604            throw new SecurityException("Call from user " + callingUserId + " as user "
605                + userId + " without permission INTERACT_ACROSS_USERS or "
606                + "INTERACT_ACROSS_USERS_FULL not allowed.");
607        }
608        if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
609            return mCurrentUserId;
610        }
611        throw new IllegalArgumentException("Calling user can be changed to only "
612                + "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
613    }
614
615    private String resolveCallingPackageNameEnforcingSecurity(String packageName) {
616        if (TextUtils.isEmpty(packageName)) {
617            return null;
618        }
619        String[] packages = mContext.getPackageManager().getPackagesForUid(
620                Binder.getCallingUid());
621        final int packageCount = packages.length;
622        for (int i = 0; i < packageCount; i++) {
623            if (packageName.equals(packages[i])) {
624                return packageName;
625            }
626        }
627        return null;
628    }
629
630    private void showEnableInstalledPrintServiceNotification(ComponentName component,
631            String label, int userId) {
632        UserHandle userHandle = new UserHandle(userId);
633
634        Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
635        intent.putExtra(EXTRA_PRINT_SERVICE_COMPONENT_NAME, component.flattenToString());
636
637        PendingIntent pendingIntent = PendingIntent.getActivityAsUser(mContext, 0, intent,
638                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT, null, userHandle);
639
640        Notification.Builder builder = new Notification.Builder(mContext)
641                .setSmallIcon(R.drawable.ic_print)
642                .setContentTitle(mContext.getString(R.string.print_service_installed_title, label))
643                .setContentText(mContext.getString(R.string.print_service_installed_message))
644                .setContentIntent(pendingIntent)
645                .setWhen(System.currentTimeMillis())
646                .setAutoCancel(true)
647                .setShowWhen(true);
648
649        NotificationManager notificationManager = (NotificationManager) mContext
650                .getSystemService(Context.NOTIFICATION_SERVICE);
651
652        String notificationTag = getClass().getName() + ":" + component.flattenToString();
653        notificationManager.notifyAsUser(notificationTag, 0, builder.build(),
654                userHandle);
655    }
656}
657