diff options
7 files changed, 203 insertions, 2 deletions
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 9329d56a8de9..0be55642d4cf 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9197,4 +9197,50 @@ public class DevicePolicyManager { throw re.rethrowFromSystemServer(); } } + + /** + * Allows/disallows printing. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @param enabled whether printing should be allowed or not. + * @throws SecurityException if {@code admin} is neither device, nor profile owner. + * @hide + */ + public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) { + try { + mService.setPrintingEnabled(admin, enabled); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Returns whether printing is enabled for current user. + * + * @return {@code true} iff printing is enabled. + * @hide + */ + public boolean isPrintingEnabled() { + try { + return mService.isPrintingEnabled(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Returns error message to be displayed when printing is disabled. + * + * Used only by PrintService. + * @return Localized error message. + * @throws SecurityException if caller is not system. + * @hide + */ + public CharSequence getPrintingDisabledReason() { + try { + return mService.getPrintingDisabledReason(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index eac7f7ed4b3e..d2a2be7bbcb5 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -396,4 +396,8 @@ interface IDevicePolicyManager { void setEndUserSessionMessage(in ComponentName admin, in CharSequence endUserSessionMessage); CharSequence getStartUserSessionMessage(in ComponentName admin); CharSequence getEndUserSessionMessage(in ComponentName admin); + + void setPrintingEnabled(in ComponentName admin, boolean enabled); + boolean isPrintingEnabled(); + CharSequence getPrintingDisabledReason(); } diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index b2fa294f77be..10c883e3af0e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -379,6 +379,9 @@ <string name="factory_reset_message">The admin app can\'t be used. Your device will now be erased.\n\nIf you have questions, contact your organization's admin.</string> + <!-- A toast message displayed when printing is attempted but disabled by policy. --> + <string name="printing_disabled_by">Printing disabled by <xliff:g id="owner_app">%s</xliff:g>.</string> + <!-- Display name for any time a piece of data refers to the owner of the phone. For example, this could be used in place of the phone's phone number. --> <string name="me">Me</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6e33648b6f14..f572d67b8862 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -878,6 +878,7 @@ <java-symbol type="string" name="preposition_for_time" /> <java-symbol type="string" name="print_service_installed_title" /> <java-symbol type="string" name="print_service_installed_message" /> + <java-symbol type="string" name="printing_disabled_by" /> <java-symbol type="string" name="progress_erasing" /> <java-symbol type="string" name="mobile_provisioning_apn" /> <java-symbol type="string" name="mobile_provisioning_url" /> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 384b416b0201..99275e88a242 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -126,4 +126,19 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public String getEndUserSessionMessage(ComponentName admin) { return null; } + + @Override + public void setPrintingEnabled(ComponentName admin, boolean enabled) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isPrintingEnabled() { + return true; + } + + @Override + public CharSequence getPrintingDisabledReason() { + return null; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index cb4f5c1177df..956b185be19e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -74,6 +74,7 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.ActivityThread; import android.app.AlarmManager; import android.app.AppGlobals; import android.app.IActivityManager; @@ -285,6 +286,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_PASSWORD_VALIDITY = "password-validity"; + private static final String TAG_PRINTING_ENABLED = "printing-enabled"; + private static final int REQUEST_EXPIRE_PASSWORD = 5571; private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1); @@ -589,6 +592,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { long mPasswordTokenHandle = 0; + boolean mPrintingEnabled = true; + public DevicePolicyData(int userHandle) { mUserHandle = userHandle; } @@ -2880,6 +2885,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { out.endTag(null, TAG_CURRENT_INPUT_METHOD_SET); } + if (!policy.mPrintingEnabled) { + out.startTag(null, TAG_PRINTING_ENABLED); + out.attribute(null, ATTR_VALUE, Boolean.toString(policy.mPrintingEnabled)); + out.endTag(null, TAG_PRINTING_ENABLED); + } + for (final String cert : policy.mOwnerInstalledCaCerts) { out.startTag(null, TAG_OWNER_INSTALLED_CA_CERT); out.attribute(null, ATTR_ALIAS, cert); @@ -3098,6 +3109,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { policy.mCurrentInputMethodSet = true; } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) { policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS)); + } else if (TAG_PRINTING_ENABLED.equals(tag)) { + String enabled = parser.getAttributeValue(null, ATTR_VALUE); + policy.mPrintingEnabled = Boolean.toString(true).equals(enabled); } else { Slog.w(LOG_TAG, "Unknown tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -12274,4 +12288,93 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return deviceOwner.endUserSessionMessage; } } + + private boolean hasPrinting() { + return mInjector.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PRINTING); + } + + @Override + public void setPrintingEnabled(ComponentName admin, boolean enabled) { + if (!mHasFeature || !hasPrinting()) { + return; + } + Preconditions.checkNotNull(admin, "Admin cannot be null."); + enforceProfileOrDeviceOwner(admin); + synchronized (this) { + final int userHandle = mInjector.userHandleGetCallingUserId(); + DevicePolicyData policy = getUserData(userHandle); + if (policy.mPrintingEnabled != enabled) { + policy.mPrintingEnabled = enabled; + saveSettingsLocked(userHandle); + } + } + } + + /** + * Returns whether printing is enabled for current user. + * @hide + */ + @Override + public boolean isPrintingEnabled() { + if (!hasPrinting()) { + return false; + } + if (!mHasFeature) { + return true; + } + synchronized (this) { + final int userHandle = mInjector.userHandleGetCallingUserId(); + DevicePolicyData policy = getUserData(userHandle); + return policy.mPrintingEnabled; + } + } + + /** + * Returns text of error message if printing is disabled. + * Only to be called by Print Service. + * @hide + */ + @Override + public CharSequence getPrintingDisabledReason() { + if (!hasPrinting() || !mHasFeature) { + Log.e(LOG_TAG, "no printing or no management"); + return null; + } + synchronized (this) { + final int userHandle = mInjector.userHandleGetCallingUserId(); + DevicePolicyData policy = getUserData(userHandle); + if (policy.mPrintingEnabled) { + Log.e(LOG_TAG, "printing is enabled"); + return null; + } + String ownerPackage = mOwners.getProfileOwnerPackage(userHandle); + if (ownerPackage == null) { + ownerPackage = mOwners.getDeviceOwnerPackageName(); + } + PackageManager pm = mInjector.getPackageManager(); + PackageInfo packageInfo; + try { + packageInfo = pm.getPackageInfo(ownerPackage, 0); + } catch (NameNotFoundException e) { + Log.e(LOG_TAG, "getPackageInfo error", e); + return null; + } + if (packageInfo == null) { + Log.e(LOG_TAG, "packageInfo is inexplicably null"); + return null; + } + ApplicationInfo appInfo = packageInfo.applicationInfo; + if (appInfo == null) { + Log.e(LOG_TAG, "appInfo is inexplicably null"); + return null; + } + CharSequence appLabel = pm.getApplicationLabel(appInfo); + if (appLabel == null) { + Log.e(LOG_TAG, "appLabel is inexplicably null"); + return null; + } + return ((Context) ActivityThread.currentActivityThread().getSystemUiContext()) + .getResources().getString(R.string.printing_disabled_by, appLabel); + } + } } diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java index cd4e8f977d60..89a5fe1b82c7 100644 --- a/services/print/java/com/android/server/print/PrintManagerService.java +++ b/services/print/java/com/android/server/print/PrintManagerService.java @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import android.annotation.NonNull; import android.app.ActivityManager; +import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -32,6 +33,7 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Binder; import android.os.Bundle; +import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; @@ -54,6 +56,7 @@ import android.service.print.PrintServiceDumpProto; import android.util.Log; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import android.widget.Toast; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; @@ -110,9 +113,12 @@ public final class PrintManagerService extends SystemService { private final SparseArray<UserState> mUserStates = new SparseArray<>(); + private final DevicePolicyManager mDpc; + PrintManagerImpl(Context context) { mContext = context; mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mDpc = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); registerContentObservers(); registerBroadcastReceivers(); } @@ -120,8 +126,26 @@ public final class PrintManagerService extends SystemService { @Override public Bundle print(String printJobName, IPrintDocumentAdapter adapter, PrintAttributes attributes, String packageName, int appId, int userId) { - printJobName = Preconditions.checkStringNotEmpty(printJobName); adapter = Preconditions.checkNotNull(adapter); + if (!isPrintingEnabled()) { + final CharSequence disabledMessage = mDpc.getPrintingDisabledReason(); + if (disabledMessage != null) { + Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage, + Toast.LENGTH_LONG).show(); + } + try { + adapter.start(); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error calling IPrintDocumentAdapter.start()"); + } + try { + adapter.finish(); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error calling IPrintDocumentAdapter.finish()"); + } + return null; + } + printJobName = Preconditions.checkStringNotEmpty(printJobName); packageName = Preconditions.checkStringNotEmpty(packageName); final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); @@ -240,7 +264,8 @@ public final class PrintManagerService extends SystemService { @Override public void restartPrintJob(PrintJobId printJobId, int appId, int userId) { - if (printJobId == null) { + if (printJobId == null || !isPrintingEnabled()) { + // if printing is disabled the state just remains "failed". return; } @@ -685,6 +710,10 @@ public final class PrintManagerService extends SystemService { } } + private boolean isPrintingEnabled() { + return mDpc == null || mDpc.isPrintingEnabled(); + } + private void dump(@NonNull DualDumpOutputStream dumpStream, @NonNull ArrayList<UserState> userStatesToDump) { final int userStateCount = userStatesToDump.size(); |