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 
17 package com.android.server.print;
18 
19 import static android.content.pm.PackageManager.GET_SERVICES;
20 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
21 import static android.content.pm.PackageManager.MATCH_INSTANT;
22 import static android.os.Process.ROOT_UID;
23 import static android.os.Process.SHELL_UID;
24 
25 import android.annotation.NonNull;
26 import android.annotation.UserIdInt;
27 import android.app.ActivityManager;
28 import android.app.admin.DevicePolicyManagerInternal;
29 import android.content.ComponentName;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.pm.PackageManager;
33 import android.content.pm.ResolveInfo;
34 import android.content.pm.UserInfo;
35 import android.database.ContentObserver;
36 import android.graphics.drawable.Icon;
37 import android.net.Uri;
38 import android.os.Binder;
39 import android.os.Bundle;
40 import android.os.Looper;
41 import android.os.Process;
42 import android.os.RemoteException;
43 import android.os.ResultReceiver;
44 import android.os.ShellCallback;
45 import android.os.UserHandle;
46 import android.os.UserManager;
47 import android.print.IPrintDocumentAdapter;
48 import android.print.IPrintJobStateChangeListener;
49 import android.print.IPrintManager;
50 import android.print.IPrintServicesChangeListener;
51 import android.print.IPrinterDiscoveryObserver;
52 import android.print.PrintAttributes;
53 import android.print.PrintJobId;
54 import android.print.PrintJobInfo;
55 import android.print.PrintManager;
56 import android.print.PrinterId;
57 import android.printservice.PrintServiceInfo;
58 import android.printservice.recommendation.IRecommendationsChangeListener;
59 import android.printservice.recommendation.RecommendationInfo;
60 import android.provider.Settings;
61 import android.service.print.PrintServiceDumpProto;
62 import android.util.Log;
63 import android.util.SparseArray;
64 import android.util.proto.ProtoOutputStream;
65 import android.widget.Toast;
66 
67 import com.android.internal.content.PackageMonitor;
68 import com.android.internal.os.BackgroundThread;
69 import com.android.internal.util.DumpUtils;
70 import com.android.internal.util.IndentingPrintWriter;
71 import com.android.internal.util.Preconditions;
72 import com.android.internal.util.dump.DualDumpOutputStream;
73 import com.android.server.LocalServices;
74 import com.android.server.SystemService;
75 import com.android.server.SystemService.TargetUser;
76 
77 import java.io.FileDescriptor;
78 import java.io.PrintWriter;
79 import java.util.ArrayList;
80 import java.util.Iterator;
81 import java.util.List;
82 import java.util.Objects;
83 
84 /**
85  * SystemService wrapper for the PrintManager implementation. Publishes
86  * Context.PRINT_SERVICE.
87  * PrintManager implementation is contained within.
88  */
89 public final class PrintManagerService extends SystemService {
90     private static final String LOG_TAG = "PrintManagerService";
91 
92     private final PrintManagerImpl mPrintManagerImpl;
93 
PrintManagerService(Context context)94     public PrintManagerService(Context context) {
95         super(context);
96         mPrintManagerImpl = new PrintManagerImpl(context);
97     }
98 
99     @Override
onStart()100     public void onStart() {
101         publishBinderService(Context.PRINT_SERVICE, mPrintManagerImpl);
102     }
103 
104     @Override
onUserUnlocking(@onNull TargetUser user)105     public void onUserUnlocking(@NonNull TargetUser user) {
106         mPrintManagerImpl.handleUserUnlocked(user.getUserIdentifier());
107     }
108 
109     @Override
onUserStopping(@onNull TargetUser user)110     public void onUserStopping(@NonNull TargetUser user) {
111         mPrintManagerImpl.handleUserStopped(user.getUserIdentifier());
112     }
113 
114     class PrintManagerImpl extends IPrintManager.Stub {
115         private static final int BACKGROUND_USER_ID = -10;
116 
117         private final Object mLock = new Object();
118 
119         private final Context mContext;
120 
121         private final UserManager mUserManager;
122 
123         private final SparseArray<UserState> mUserStates = new SparseArray<>();
124 
PrintManagerImpl(Context context)125         PrintManagerImpl(Context context) {
126             mContext = context;
127             mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
128             registerContentObservers();
129             registerBroadcastReceivers();
130         }
131 
132         @Override
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)133         public void onShellCommand(FileDescriptor in, FileDescriptor out,
134                 FileDescriptor err, String[] args, ShellCallback callback,
135                 ResultReceiver resultReceiver) {
136             new PrintShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
137         }
138 
139         @Override
print(String printJobName, IPrintDocumentAdapter adapter, PrintAttributes attributes, String packageName, int appId, int userId)140         public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
141                 PrintAttributes attributes, String packageName, int appId, int userId) {
142             Objects.requireNonNull(adapter);
143             if (!isPrintingEnabled()) {
144                 CharSequence disabledMessage = null;
145                 DevicePolicyManagerInternal dpmi =
146                         LocalServices.getService(DevicePolicyManagerInternal.class);
147                 final int callingUserId = UserHandle.getCallingUserId();
148                 final long identity = Binder.clearCallingIdentity();
149                 try {
150                     disabledMessage = dpmi.getPrintingDisabledReasonForUser(callingUserId);
151 
152                     if (disabledMessage != null) {
153                         Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage,
154                                 Toast.LENGTH_LONG).show();
155                     }
156                 } finally {
157                     Binder.restoreCallingIdentity(identity);
158                 }
159                 try {
160                     adapter.start();
161                 } catch (RemoteException re) {
162                     Log.e(LOG_TAG, "Error calling IPrintDocumentAdapter.start()");
163                 }
164                 try {
165                     adapter.finish();
166                 } catch (RemoteException re) {
167                     Log.e(LOG_TAG, "Error calling IPrintDocumentAdapter.finish()");
168                 }
169                 return null;
170             }
171             printJobName = Preconditions.checkStringNotEmpty(printJobName);
172             packageName = Preconditions.checkStringNotEmpty(packageName);
173 
174             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
175             final int resolvedAppId;
176             final UserState userState;
177             final String resolvedPackageName;
178             synchronized (mLock) {
179                 // Only the current group members can start new print jobs.
180                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
181                     return null;
182                 }
183                 resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
184                 resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName);
185                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
186             }
187             final long identity = Binder.clearCallingIdentity();
188             try {
189                 return userState.print(printJobName, adapter, attributes,
190                         resolvedPackageName, resolvedAppId);
191             } finally {
192                 Binder.restoreCallingIdentity(identity);
193             }
194         }
195 
196         @Override
getPrintJobInfos(int appId, int userId)197         public List<PrintJobInfo> getPrintJobInfos(int appId, int userId) {
198             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
199             final int resolvedAppId;
200             final UserState userState;
201             synchronized (mLock) {
202                 // Only the current group members can query for state of print jobs.
203                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
204                     return null;
205                 }
206                 resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
207                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
208             }
209             final long identity = Binder.clearCallingIdentity();
210             try {
211                 return userState.getPrintJobInfos(resolvedAppId);
212             } finally {
213                 Binder.restoreCallingIdentity(identity);
214             }
215         }
216 
217         @Override
getPrintJobInfo(PrintJobId printJobId, int appId, int userId)218         public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId, int userId) {
219             if (printJobId == null) {
220                 return null;
221             }
222 
223             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
224             final int resolvedAppId;
225             final UserState userState;
226             synchronized (mLock) {
227                 // Only the current group members can query for state of a print job.
228                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
229                     return null;
230                 }
231                 resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
232                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
233             }
234             final long identity = Binder.clearCallingIdentity();
235             try {
236                 return userState.getPrintJobInfo(printJobId, resolvedAppId);
237             } finally {
238                 Binder.restoreCallingIdentity(identity);
239             }
240         }
241 
242         @Override
getCustomPrinterIcon(PrinterId printerId, int userId)243         public Icon getCustomPrinterIcon(PrinterId printerId, int userId) {
244             Objects.requireNonNull(printerId);
245 
246             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
247             final UserState userState;
248             synchronized (mLock) {
249                 // Only the current group members can get the printer icons.
250                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
251                     return null;
252                 }
253                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
254             }
255             final long identity = Binder.clearCallingIdentity();
256             try {
257                 Icon icon = userState.getCustomPrinterIcon(printerId);
258                 return validateIconUserBoundary(icon);
259             } finally {
260                 Binder.restoreCallingIdentity(identity);
261             }
262         }
263 
264         /**
265          * Validates the custom printer icon to see if it's not in the calling user space.
266          * If the condition is not met, return null. Otherwise, return the original icon.
267          *
268          * @param icon
269          * @return icon (validated)
270          */
validateIconUserBoundary(Icon icon)271         private Icon validateIconUserBoundary(Icon icon) {
272             // Refer to Icon#getUriString for context. The URI string is invalid for icons of
273             // incompatible types.
274             if (icon != null && (icon.getType() == Icon.TYPE_URI
275                     || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
276                 String encodedUser = icon.getUri().getEncodedUserInfo();
277 
278                 // If there is no encoded user, the URI is calling into the calling user space
279                 if (encodedUser != null) {
280                     int userId = Integer.parseInt(encodedUser);
281                     // resolve encoded user
282                     final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
283 
284                     synchronized (mLock) {
285                         // Only the current group members can get the printer icons.
286                         if (resolveCallingProfileParentLocked(resolvedUserId)
287                                 != getCurrentUserId()) {
288                             return null;
289                         }
290                     }
291                 }
292             }
293             return icon;
294         }
295 
296         @Override
cancelPrintJob(PrintJobId printJobId, int appId, int userId)297         public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) {
298             if (printJobId == null) {
299                 return;
300             }
301 
302             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
303             final int resolvedAppId;
304             final UserState userState;
305             synchronized (mLock) {
306                 // Only the current group members can cancel a print job.
307                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
308                     return;
309                 }
310                 resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
311                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
312             }
313             final long identity = Binder.clearCallingIdentity();
314             try {
315                 userState.cancelPrintJob(printJobId, resolvedAppId);
316             } finally {
317                 Binder.restoreCallingIdentity(identity);
318             }
319         }
320 
321         @Override
restartPrintJob(PrintJobId printJobId, int appId, int userId)322         public void restartPrintJob(PrintJobId printJobId, int appId, int userId) {
323             if (printJobId == null || !isPrintingEnabled()) {
324                 // if printing is disabled the state just remains "failed".
325                 return;
326             }
327 
328             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
329             final int resolvedAppId;
330             final UserState userState;
331             synchronized (mLock) {
332                 // Only the current group members can restart a print job.
333                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
334                     return;
335                 }
336                 resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
337                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
338             }
339             final long identity = Binder.clearCallingIdentity();
340             try {
341                 userState.restartPrintJob(printJobId, resolvedAppId);
342             } finally {
343                 Binder.restoreCallingIdentity(identity);
344             }
345         }
346 
347         @Override
getPrintServices(int selectionFlags, int userId)348         public List<PrintServiceInfo> getPrintServices(int selectionFlags, int userId) {
349             Preconditions.checkFlagsArgument(selectionFlags,
350                     PrintManager.DISABLED_SERVICES | PrintManager.ENABLED_SERVICES);
351 
352             mContext.enforceCallingOrSelfPermission(
353                     android.Manifest.permission.READ_PRINT_SERVICES, null);
354             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
355             final UserState userState;
356             synchronized (mLock) {
357                 // Only the current group members can get print services.
358                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
359                     return null;
360                 }
361                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
362             }
363             final long identity = Binder.clearCallingIdentity();
364             try {
365                 return userState.getPrintServices(selectionFlags);
366             } finally {
367                 Binder.restoreCallingIdentity(identity);
368             }
369         }
370 
371         @Override
setPrintServiceEnabled(ComponentName service, boolean isEnabled, int userId)372         public void setPrintServiceEnabled(ComponentName service, boolean isEnabled, int userId) {
373             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
374             final int appId = UserHandle.getAppId(Binder.getCallingUid());
375 
376             try {
377                 if (appId != Process.SYSTEM_UID && appId != UserHandle.getAppId(
378                         mContext.getPackageManager().getPackageUidAsUser(
379                                 PrintManager.PRINT_SPOOLER_PACKAGE_NAME, resolvedUserId))) {
380                     throw new SecurityException("Only system and print spooler can call this");
381                 }
382             } catch (PackageManager.NameNotFoundException e) {
383                 Log.e(LOG_TAG, "Could not verify caller", e);
384                 return;
385             }
386 
387             Objects.requireNonNull(service);
388 
389             final UserState userState;
390             synchronized (mLock) {
391                 // Only the current group members can enable / disable services.
392                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
393                     return;
394                 }
395                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
396             }
397             final long identity = Binder.clearCallingIdentity();
398             try {
399                 userState.setPrintServiceEnabled(service, isEnabled);
400             } finally {
401                 Binder.restoreCallingIdentity(identity);
402             }
403         }
404 
405         @Override
isPrintServiceEnabled(ComponentName service, int userId)406         public boolean isPrintServiceEnabled(ComponentName service, int userId) {
407             final String[] packages = mContext.getPackageManager().getPackagesForUid(
408                     Binder.getCallingUid());
409             boolean matchCalling = false;
410             for (int i = 0; i < packages.length; i++) {
411                 if (packages[i].equals(service.getPackageName())) {
412                     matchCalling = true;
413                     break;
414                 }
415             }
416             if (!matchCalling) {
417                 // Do not reveal any information about other package services.
418                 throw new SecurityException("PrintService does not share UID with caller.");
419             }
420             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
421             final UserState userState;
422             synchronized (mLock) {
423                 // Only the current group members can check print services.
424                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
425                     return false;
426                 }
427                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
428             }
429             return userState.isPrintServiceEnabled(service);
430         }
431 
432         @Override
getPrintServiceRecommendations(int userId)433         public List<RecommendationInfo> getPrintServiceRecommendations(int userId) {
434             mContext.enforceCallingOrSelfPermission(
435                     android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
436             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
437             final UserState userState;
438             synchronized (mLock) {
439                 // Only the current group members can get print service recommendations.
440                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
441                     return null;
442                 }
443                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
444             }
445             final long identity = Binder.clearCallingIdentity();
446             try {
447                 return userState.getPrintServiceRecommendations();
448             } finally {
449                 Binder.restoreCallingIdentity(identity);
450             }
451         }
452 
453         @Override
createPrinterDiscoverySession(IPrinterDiscoveryObserver observer, int userId)454         public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
455                 int userId) {
456             Objects.requireNonNull(observer);
457 
458             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
459             final UserState userState;
460             synchronized (mLock) {
461                 // Only the current group members can create a discovery session.
462                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
463                     return;
464                 }
465                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
466             }
467             final long identity = Binder.clearCallingIdentity();
468             try {
469                 userState.createPrinterDiscoverySession(observer);
470             } finally {
471                 Binder.restoreCallingIdentity(identity);
472             }
473         }
474 
475         @Override
destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer, int userId)476         public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
477                 int userId) {
478             Objects.requireNonNull(observer);
479 
480             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
481             final UserState userState;
482             synchronized (mLock) {
483                 // Only the current group members can destroy a discovery session.
484                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
485                     return;
486                 }
487                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
488             }
489             final long identity = Binder.clearCallingIdentity();
490             try {
491                 userState.destroyPrinterDiscoverySession(observer);
492             } finally {
493                 Binder.restoreCallingIdentity(identity);
494             }
495         }
496 
497         @Override
startPrinterDiscovery(IPrinterDiscoveryObserver observer, List<PrinterId> priorityList, int userId)498         public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
499                 List<PrinterId> priorityList, int userId) {
500             Objects.requireNonNull(observer);
501             if (priorityList != null) {
502                 priorityList = Preconditions.checkCollectionElementsNotNull(priorityList,
503                         "PrinterId");
504             }
505 
506             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
507             final UserState userState;
508             synchronized (mLock) {
509                 // Only the current group members can start discovery.
510                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
511                     return;
512                 }
513                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
514             }
515             final long identity = Binder.clearCallingIdentity();
516             try {
517                 userState.startPrinterDiscovery(observer, priorityList);
518             } finally {
519                 Binder.restoreCallingIdentity(identity);
520             }
521         }
522 
523         @Override
stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId)524         public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) {
525             Objects.requireNonNull(observer);
526 
527             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
528             final UserState userState;
529             synchronized (mLock) {
530                 // Only the current group members can stop discovery.
531                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
532                     return;
533                 }
534                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
535             }
536             final long identity = Binder.clearCallingIdentity();
537             try {
538                 userState.stopPrinterDiscovery(observer);
539             } finally {
540                 Binder.restoreCallingIdentity(identity);
541             }
542         }
543 
544         @Override
validatePrinters(List<PrinterId> printerIds, int userId)545         public void validatePrinters(List<PrinterId> printerIds, int userId) {
546             printerIds = Preconditions.checkCollectionElementsNotNull(printerIds, "PrinterId");
547 
548             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
549             final UserState userState;
550             synchronized (mLock) {
551                 // Only the current group members can validate printers.
552                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
553                     return;
554                 }
555                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
556             }
557             final long identity = Binder.clearCallingIdentity();
558             try {
559                 userState.validatePrinters(printerIds);
560             } finally {
561                 Binder.restoreCallingIdentity(identity);
562             }
563         }
564 
565         @Override
startPrinterStateTracking(PrinterId printerId, int userId)566         public void startPrinterStateTracking(PrinterId printerId, int userId) {
567             Objects.requireNonNull(printerId);
568 
569             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
570             final UserState userState;
571             synchronized (mLock) {
572                 // Only the current group members can start printer tracking.
573                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
574                     return;
575                 }
576                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
577             }
578             final long identity = Binder.clearCallingIdentity();
579             try {
580                 userState.startPrinterStateTracking(printerId);
581             } finally {
582                 Binder.restoreCallingIdentity(identity);
583             }
584         }
585 
586         @Override
stopPrinterStateTracking(PrinterId printerId, int userId)587         public void stopPrinterStateTracking(PrinterId printerId, int userId) {
588             Objects.requireNonNull(printerId);
589 
590             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
591             final UserState userState;
592             synchronized (mLock) {
593                 // Only the current group members can stop printer tracking.
594                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
595                     return;
596                 }
597                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
598             }
599             final long identity = Binder.clearCallingIdentity();
600             try {
601                 userState.stopPrinterStateTracking(printerId);
602             } finally {
603                 Binder.restoreCallingIdentity(identity);
604             }
605         }
606 
607         @Override
addPrintJobStateChangeListener(IPrintJobStateChangeListener listener, int appId, int userId)608         public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
609                 int appId, int userId) throws RemoteException {
610             Objects.requireNonNull(listener);
611 
612             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
613             final int resolvedAppId;
614             final UserState userState;
615             synchronized (mLock) {
616                 // Only the current group members can add a print job listener.
617                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
618                     return;
619                 }
620                 resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
621                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
622             }
623             final long identity = Binder.clearCallingIdentity();
624             try {
625                 userState.addPrintJobStateChangeListener(listener, resolvedAppId);
626             } finally {
627                 Binder.restoreCallingIdentity(identity);
628             }
629         }
630 
631         @Override
removePrintJobStateChangeListener(IPrintJobStateChangeListener listener, int userId)632         public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener,
633                 int userId) {
634             Objects.requireNonNull(listener);
635 
636             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
637             final UserState userState;
638             synchronized (mLock) {
639                 // Only the current group members can remove a print job listener.
640                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
641                     return;
642                 }
643                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
644             }
645             final long identity = Binder.clearCallingIdentity();
646             try {
647                 userState.removePrintJobStateChangeListener(listener);
648             } finally {
649                 Binder.restoreCallingIdentity(identity);
650             }
651         }
652 
653         @Override
addPrintServicesChangeListener(IPrintServicesChangeListener listener, int userId)654         public void addPrintServicesChangeListener(IPrintServicesChangeListener listener,
655                 int userId) throws RemoteException {
656             Objects.requireNonNull(listener);
657 
658             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRINT_SERVICES,
659                     null);
660             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
661             final UserState userState;
662             synchronized (mLock) {
663                 // Only the current group members can add a print services listener.
664                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
665                     return;
666                 }
667                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
668             }
669             final long identity = Binder.clearCallingIdentity();
670             try {
671                 userState.addPrintServicesChangeListener(listener);
672             } finally {
673                 Binder.restoreCallingIdentity(identity);
674             }
675         }
676 
677         @Override
removePrintServicesChangeListener(IPrintServicesChangeListener listener, int userId)678         public void removePrintServicesChangeListener(IPrintServicesChangeListener listener,
679                 int userId) {
680             Objects.requireNonNull(listener);
681 
682             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRINT_SERVICES,
683                     null);
684             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
685             final UserState userState;
686             synchronized (mLock) {
687                 // Only the current group members can remove a print services change listener.
688                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
689                     return;
690                 }
691                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
692             }
693             final long identity = Binder.clearCallingIdentity();
694             try {
695                 userState.removePrintServicesChangeListener(listener);
696             } finally {
697                 Binder.restoreCallingIdentity(identity);
698             }
699         }
700 
701         @Override
addPrintServiceRecommendationsChangeListener( IRecommendationsChangeListener listener, int userId)702         public void addPrintServiceRecommendationsChangeListener(
703                 IRecommendationsChangeListener listener, int userId)
704                 throws RemoteException {
705             Objects.requireNonNull(listener);
706 
707             mContext.enforceCallingOrSelfPermission(
708                     android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
709             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
710             final UserState userState;
711             synchronized (mLock) {
712                 // Only the current group members can add a print service recommendations listener.
713                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
714                     return;
715                 }
716                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
717             }
718             final long identity = Binder.clearCallingIdentity();
719             try {
720                 userState.addPrintServiceRecommendationsChangeListener(listener);
721             } finally {
722                 Binder.restoreCallingIdentity(identity);
723             }
724         }
725 
726         @Override
removePrintServiceRecommendationsChangeListener( IRecommendationsChangeListener listener, int userId)727         public void removePrintServiceRecommendationsChangeListener(
728                 IRecommendationsChangeListener listener, int userId) {
729             Objects.requireNonNull(listener);
730 
731             mContext.enforceCallingOrSelfPermission(
732                     android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
733             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
734             final UserState userState;
735             synchronized (mLock) {
736                 // Only the current group members can remove a print service recommendations
737                 // listener.
738                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
739                     return;
740                 }
741                 userState = getOrCreateUserStateLocked(resolvedUserId, false);
742             }
743             final long identity = Binder.clearCallingIdentity();
744             try {
745                 userState.removePrintServiceRecommendationsChangeListener(listener);
746             } finally {
747                 Binder.restoreCallingIdentity(identity);
748             }
749         }
750 
751         @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)752         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
753             Objects.requireNonNull(fd);
754 
755             if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
756 
757             int opti = 0;
758             boolean dumpAsProto = false;
759             while (opti < args.length) {
760                 String opt = args[opti];
761                 if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
762                     break;
763                 }
764                 opti++;
765                 if ("--proto".equals(opt)) {
766                     dumpAsProto = true;
767                 } else {
768                     pw.println("Unknown argument: " + opt + "; use -h for help");
769                 }
770             }
771 
772             ArrayList<UserState> userStatesToDump = new ArrayList<>();
773             synchronized (mLock) {
774                 int numUserStates = mUserStates.size();
775                 for (int i = 0; i < numUserStates; i++) {
776                     userStatesToDump.add(mUserStates.valueAt(i));
777                 }
778             }
779 
780             final long identity = Binder.clearCallingIdentity();
781             try {
782                 if (dumpAsProto) {
783                     dump(new DualDumpOutputStream(new ProtoOutputStream(fd)),
784                             userStatesToDump);
785                 } else {
786                     pw.println("PRINT MANAGER STATE (dumpsys print)");
787 
788                     dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, "  ")),
789                             userStatesToDump);
790                 }
791             } finally {
792                 Binder.restoreCallingIdentity(identity);
793             }
794         }
795 
796         @Override
getBindInstantServiceAllowed(@serIdInt int userId)797         public boolean getBindInstantServiceAllowed(@UserIdInt int userId) {
798             int callingUid = Binder.getCallingUid();
799             if (callingUid != SHELL_UID && callingUid != ROOT_UID) {
800                 throw new SecurityException("Can only be called by uid " + SHELL_UID
801                         + " or " + ROOT_UID);
802             }
803 
804             final UserState userState;
805             synchronized (mLock) {
806                 userState = getOrCreateUserStateLocked(userId, false);
807             }
808             final long identity = Binder.clearCallingIdentity();
809             try {
810                 return userState.getBindInstantServiceAllowed();
811             } finally {
812                 Binder.restoreCallingIdentity(identity);
813             }
814         }
815 
816         @Override
setBindInstantServiceAllowed(@serIdInt int userId, boolean allowed)817         public void setBindInstantServiceAllowed(@UserIdInt int userId, boolean allowed) {
818             int callingUid = Binder.getCallingUid();
819             if (callingUid != SHELL_UID && callingUid != ROOT_UID) {
820                 throw new SecurityException("Can only be called by uid " + SHELL_UID
821                         + " or " + ROOT_UID);
822             }
823 
824             final UserState userState;
825             synchronized (mLock) {
826                 userState = getOrCreateUserStateLocked(userId, false);
827             }
828             final long identity = Binder.clearCallingIdentity();
829             try {
830                 userState.setBindInstantServiceAllowed(allowed);
831             } finally {
832                 Binder.restoreCallingIdentity(identity);
833             }
834         }
835 
isPrintingEnabled()836         private boolean isPrintingEnabled() {
837             return !mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING,
838                     Binder.getCallingUserHandle());
839         }
840 
dump(@onNull DualDumpOutputStream dumpStream, @NonNull ArrayList<UserState> userStatesToDump)841         private void dump(@NonNull DualDumpOutputStream dumpStream,
842                 @NonNull ArrayList<UserState> userStatesToDump) {
843             final int userStateCount = userStatesToDump.size();
844             for (int i = 0; i < userStateCount; i++) {
845                 long token = dumpStream.start("user_states", PrintServiceDumpProto.USER_STATES);
846                 userStatesToDump.get(i).dump(dumpStream);
847                 dumpStream.end(token);
848             }
849 
850             dumpStream.flush();
851         }
852 
registerContentObservers()853         private void registerContentObservers() {
854             final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
855                     Settings.Secure.DISABLED_PRINT_SERVICES);
856             ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
857                 @Override
858                 public void onChange(boolean selfChange, Uri uri, int userId) {
859                     if (enabledPrintServicesUri.equals(uri)) {
860                         synchronized (mLock) {
861                             final int userCount = mUserStates.size();
862                             for (int i = 0; i < userCount; i++) {
863                                 if (userId == UserHandle.USER_ALL
864                                         || userId == mUserStates.keyAt(i)) {
865                                     mUserStates.valueAt(i).updateIfNeededLocked();
866                                 }
867                             }
868                         }
869                     }
870                 }
871             };
872 
873             mContext.getContentResolver().registerContentObserver(enabledPrintServicesUri,
874                     false, observer, UserHandle.USER_ALL);
875         }
876 
registerBroadcastReceivers()877         private void registerBroadcastReceivers() {
878             PackageMonitor monitor = new PackageMonitor(true) {
879                 /**
880                  * Checks if the package contains a print service.
881                  *
882                  * @param packageName The name of the package
883                  *
884                  * @return true iff the package contains a print service
885                  */
886                 private boolean hasPrintService(String packageName) {
887                     Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
888                     intent.setPackage(packageName);
889 
890                     List<ResolveInfo> installedServices = mContext.getPackageManager()
891                             .queryIntentServicesAsUser(intent,
892                                     GET_SERVICES | MATCH_DEBUG_TRIAGED_MISSING | MATCH_INSTANT,
893                                     getChangingUserId());
894 
895                     return installedServices != null && !installedServices.isEmpty();
896                 }
897 
898                 /**
899                  * Checks if there is a print service currently registered for this package.
900                  *
901                  * @param userState The userstate for the current user
902                  * @param packageName The name of the package
903                  *
904                  * @return true iff the package contained (and might still contain) a print service
905                  */
906                 private boolean hadPrintService(@NonNull UserState userState, String packageName) {
907                     List<PrintServiceInfo> installedServices = userState
908                             .getPrintServices(PrintManager.ALL_SERVICES);
909 
910                     if (installedServices == null) {
911                         return false;
912                     }
913 
914                     final int numInstalledServices = installedServices.size();
915                     for (int i = 0; i < numInstalledServices; i++) {
916                         if (installedServices.get(i).getResolveInfo().serviceInfo.packageName
917                                 .equals(packageName)) {
918                             return true;
919                         }
920                     }
921 
922                     return false;
923                 }
924 
925                 @Override
926                 public void onPackageModified(String packageName) {
927                     if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return;
928                     UserState userState = getOrCreateUserStateLocked(getChangingUserId(), false,
929                             false /* enforceUserUnlockingOrUnlocked */);
930 
931                     boolean prunePrintServices = false;
932                     synchronized (mLock) {
933                         if (hadPrintService(userState, packageName)
934                                 || hasPrintService(packageName)) {
935                             userState.updateIfNeededLocked();
936                             prunePrintServices = true;
937                         }
938                     }
939 
940                     if (prunePrintServices) {
941                         userState.prunePrintServices();
942                     }
943                 }
944 
945                 @Override
946                 public void onPackageRemoved(String packageName, int uid) {
947                     if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return;
948                     UserState userState = getOrCreateUserStateLocked(getChangingUserId(), false,
949                             false /* enforceUserUnlockingOrUnlocked */);
950 
951                     boolean prunePrintServices = false;
952                     synchronized (mLock) {
953                         if (hadPrintService(userState, packageName)) {
954                             userState.updateIfNeededLocked();
955                             prunePrintServices = true;
956                         }
957                     }
958 
959                     if (prunePrintServices) {
960                         userState.prunePrintServices();
961                     }
962                 }
963 
964                 @Override
965                 public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
966                         int uid, boolean doit) {
967                     if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return false;
968                     synchronized (mLock) {
969                         // A background user/profile's print jobs are running but there is
970                         // no UI shown. Hence, if the packages of such a user change we need
971                         // to handle it as the change may affect ongoing print jobs.
972                         UserState userState = getOrCreateUserStateLocked(getChangingUserId(), false,
973                                 false /* enforceUserUnlockingOrUnlocked */);
974                         boolean stoppedSomePackages = false;
975 
976                         List<PrintServiceInfo> enabledServices = userState
977                                 .getPrintServices(PrintManager.ENABLED_SERVICES);
978                         if (enabledServices == null) {
979                             return false;
980                         }
981 
982                         Iterator<PrintServiceInfo> iterator = enabledServices.iterator();
983                         while (iterator.hasNext()) {
984                             ComponentName componentName = iterator.next().getComponentName();
985                             String componentPackage = componentName.getPackageName();
986                             for (String stoppedPackage : stoppedPackages) {
987                                 if (componentPackage.equals(stoppedPackage)) {
988                                     if (!doit) {
989                                         return true;
990                                     }
991                                     stoppedSomePackages = true;
992                                     break;
993                                 }
994                             }
995                         }
996                         if (stoppedSomePackages) {
997                             userState.updateIfNeededLocked();
998                         }
999                         return false;
1000                     }
1001                 }
1002 
1003                 @Override
1004                 public void onPackageAdded(String packageName, int uid) {
1005                     if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return;
1006                     synchronized (mLock) {
1007                         if (hasPrintService(packageName)) {
1008                             UserState userState = getOrCreateUserStateLocked(getChangingUserId(),
1009                                     false, false /* enforceUserUnlockingOrUnlocked */);
1010                             userState.updateIfNeededLocked();
1011                         }
1012                     }
1013                 }
1014             };
1015 
1016             // package changes
1017             monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
1018                     UserHandle.ALL, true);
1019         }
1020 
getOrCreateUserStateLocked(int userId, boolean lowPriority)1021         private UserState getOrCreateUserStateLocked(int userId, boolean lowPriority) {
1022             return getOrCreateUserStateLocked(userId, lowPriority,
1023                     true /* enforceUserUnlockingOrUnlocked */);
1024         }
1025 
getOrCreateUserStateLocked(int userId, boolean lowPriority, boolean enforceUserUnlockingOrUnlocked)1026         private UserState getOrCreateUserStateLocked(int userId, boolean lowPriority,
1027                 boolean enforceUserUnlockingOrUnlocked) {
1028             return getOrCreateUserStateLocked(userId, lowPriority,
1029                     enforceUserUnlockingOrUnlocked, false /* shouldUpdateState */);
1030         }
1031 
getOrCreateUserStateLocked(int userId, boolean lowPriority, boolean enforceUserUnlockingOrUnlocked, boolean shouldUpdateState)1032         private UserState getOrCreateUserStateLocked(int userId, boolean lowPriority,
1033                 boolean enforceUserUnlockingOrUnlocked, boolean shouldUpdateState) {
1034             if (enforceUserUnlockingOrUnlocked && !mUserManager.isUserUnlockingOrUnlocked(userId)) {
1035                 throw new IllegalStateException(
1036                         "User " + userId + " must be unlocked for printing to be available");
1037             }
1038 
1039             UserState userState = mUserStates.get(userId);
1040             if (userState == null) {
1041                 userState = new UserState(mContext, userId, mLock, lowPriority);
1042                 mUserStates.put(userId, userState);
1043             } else if (shouldUpdateState) {
1044                 userState.updateIfNeededLocked();
1045             }
1046 
1047             if (!lowPriority) {
1048                 userState.increasePriority();
1049             }
1050 
1051             return userState;
1052         }
1053 
handleUserUnlocked(final int userId)1054         private void handleUserUnlocked(final int userId) {
1055             // This code will touch the remote print spooler which
1056             // must be called off the main thread, so post the work.
1057             BackgroundThread.getHandler().post(new Runnable() {
1058                 @Override
1059                 public void run() {
1060                     if (!mUserManager.isUserUnlockingOrUnlocked(userId)) return;
1061 
1062                     UserState userState;
1063                     synchronized (mLock) {
1064                         userState = getOrCreateUserStateLocked(userId, /* lowPriority */ true,
1065                                 /* enforceUserUnlockingOrUnlocked */ false,
1066                                 /* shouldUpdateState */ true);
1067                     }
1068                     // This is the first time we switch to this user after boot, so
1069                     // now is the time to remove obsolete print jobs since they
1070                     // are from the last boot and no application would query them.
1071                     userState.removeObsoletePrintJobs();
1072                 }
1073             });
1074         }
1075 
handleUserStopped(final int userId)1076         private void handleUserStopped(final int userId) {
1077             // This code will touch the remote print spooler which
1078             // must be called off the main thread, so post the work.
1079             BackgroundThread.getHandler().post(new Runnable() {
1080                 @Override
1081                 public void run() {
1082                     synchronized (mLock) {
1083                         UserState userState = mUserStates.get(userId);
1084                         if (userState != null) {
1085                             userState.destroyLocked();
1086                             mUserStates.remove(userId);
1087                         }
1088                     }
1089                 }
1090             });
1091         }
1092 
resolveCallingProfileParentLocked(int userId)1093         private int resolveCallingProfileParentLocked(int userId) {
1094             if (userId != getCurrentUserId()) {
1095                 final long identity = Binder.clearCallingIdentity();
1096                 try {
1097                     UserInfo parent = mUserManager.getProfileParent(userId);
1098                     if (parent != null) {
1099                         return parent.getUserHandle().getIdentifier();
1100                     } else {
1101                         return BACKGROUND_USER_ID;
1102                     }
1103                 } finally {
1104                     Binder.restoreCallingIdentity(identity);
1105                 }
1106             }
1107             return userId;
1108         }
1109 
resolveCallingAppEnforcingPermissions(int appId)1110         private int resolveCallingAppEnforcingPermissions(int appId) {
1111             final int callingUid = Binder.getCallingUid();
1112             if (callingUid == 0) {
1113                 return appId;
1114             }
1115             final int callingAppId = UserHandle.getAppId(callingUid);
1116             if (appId == callingAppId || callingAppId == SHELL_UID
1117                     || callingAppId == Process.SYSTEM_UID) {
1118                 return appId;
1119             }
1120             if (mContext.checkCallingPermission(
1121                     "com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS")
1122                     != PackageManager.PERMISSION_GRANTED) {
1123                 throw new SecurityException("Call from app " + callingAppId + " as app "
1124                         + appId + " without com.android.printspooler.permission"
1125                         + ".ACCESS_ALL_PRINT_JOBS");
1126             }
1127             return appId;
1128         }
1129 
resolveCallingUserEnforcingPermissions(int userId)1130         private int resolveCallingUserEnforcingPermissions(int userId) {
1131             try {
1132                 return ActivityManager.getService().handleIncomingUser(Binder.getCallingPid(),
1133                         Binder.getCallingUid(), userId, true, true, "", null);
1134             } catch (RemoteException re) {
1135                 // Shouldn't happen, local.
1136             }
1137             return userId;
1138         }
1139 
resolveCallingPackageNameEnforcingSecurity( @onNull String packageName)1140         private @NonNull String resolveCallingPackageNameEnforcingSecurity(
1141                 @NonNull String packageName) {
1142             String[] packages = mContext.getPackageManager().getPackagesForUid(
1143                     Binder.getCallingUid());
1144             final int packageCount = packages.length;
1145             for (int i = 0; i < packageCount; i++) {
1146                 if (packageName.equals(packages[i])) {
1147                     return packageName;
1148                 }
1149             }
1150             throw new IllegalArgumentException("packageName has to belong to the caller");
1151         }
1152 
getCurrentUserId()1153         private int getCurrentUserId () {
1154             final long identity = Binder.clearCallingIdentity();
1155             try {
1156                 return ActivityManager.getCurrentUser();
1157             } finally {
1158                 Binder.restoreCallingIdentity(identity);
1159             }
1160         }
1161     }
1162 }
1163