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