From b3baaab4af7e1ed8dfc5da1c976cc34d135fb82b Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Thu, 10 May 2018 15:22:12 -0700 Subject: [PATCH] Allow Print subsystem to work with services provided by instant app Most functionality works, but the PrintActivity cannot directly interact with the instant service. As instant services should only appear in tests this functionality needs to be enabled via shell commands. Fixes: 79484768 Test: cts-tradefed run commandAndExit cts-instant-dev -m CtsPrintTestCases cts-tradefed run commandAndExit cts-dev -m CtsPrintTestCases Change-Id: Ie4663c72b8c0f959b5d172ef28875479d120e386 --- core/java/android/print/IPrintManager.aidl | 17 +++ .../printspooler/ui/PrintActivity.java | 2 + .../server/print/PrintManagerService.java | 57 ++++++++- .../server/print/PrintShellCommand.java | 112 ++++++++++++++++++ .../server/print/RemotePrintService.java | 4 +- .../com/android/server/print/UserState.java | 27 ++++- 6 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 services/print/java/com/android/server/print/PrintShellCommand.java diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl index d7c267b5ca63b..d3d38744a1a93 100644 --- a/core/java/android/print/IPrintManager.aidl +++ b/core/java/android/print/IPrintManager.aidl @@ -143,4 +143,21 @@ interface IPrintManager { void stopPrinterStateTracking(in PrinterId printerId, int userId); void destroyPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId); + + /** + * Check if the system will bind to print services in intant app. + * + * @param userId the Id of the user the behavior should be checked for + * + * @return {@code true} iff the system will bind to print services in instant apps. + */ + boolean getBindInstantServiceAllowed(int userId); + + /** + * Set if the system will bind to print services in intant app. + * + * @param userId the Id of the user the behavior should be changed for + * @param allows iff {@code true} the system will bind to print services in instant apps + */ + void setBindInstantServiceAllowed(int userId, boolean allowed); } diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index 83d7e1666809c..06fbf9f6e1c13 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -811,6 +811,8 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat List resolvedActivities = getPackageManager() .queryIntentActivities(intent, 0); if (resolvedActivities.isEmpty()) { + Log.w(LOG_TAG, "Advanced options activity " + mAdvancedPrintOptionsActivity + " could " + + "not be found"); return; } diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java index 83a125d1fc366..dc55179bdc9e8 100644 --- a/services/print/java/com/android/server/print/PrintManagerService.java +++ b/services/print/java/com/android/server/print/PrintManagerService.java @@ -18,8 +18,12 @@ package com.android.server.print; import static android.content.pm.PackageManager.GET_SERVICES; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; +import static android.content.pm.PackageManager.MATCH_INSTANT; +import static android.os.Process.ROOT_UID; +import static android.os.Process.SHELL_UID; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.admin.DevicePolicyManagerInternal; import android.content.ComponentName; @@ -36,6 +40,8 @@ import android.os.Bundle; import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManager; import android.print.IPrintDocumentAdapter; @@ -121,6 +127,13 @@ public final class PrintManagerService extends SystemService { registerBroadcastReceivers(); } + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, + FileDescriptor err, String[] args, ShellCallback callback, + ResultReceiver resultReceiver) { + new PrintShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver); + } + @Override public Bundle print(String printJobName, IPrintDocumentAdapter adapter, PrintAttributes attributes, String packageName, int appId, int userId) { @@ -717,6 +730,46 @@ public final class PrintManagerService extends SystemService { } } + @Override + public boolean getBindInstantServiceAllowed(@UserIdInt int userId) { + int callingUid = Binder.getCallingUid(); + if (callingUid != SHELL_UID && callingUid != ROOT_UID) { + throw new SecurityException("Can only be called by uid " + SHELL_UID + + " or " + ROOT_UID); + } + + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(userId, false); + } + final long identity = Binder.clearCallingIdentity(); + try { + return userState.getBindInstantServiceAllowed(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void setBindInstantServiceAllowed(@UserIdInt int userId, boolean allowed) { + int callingUid = Binder.getCallingUid(); + if (callingUid != SHELL_UID && callingUid != ROOT_UID) { + throw new SecurityException("Can only be called by uid " + SHELL_UID + + " or " + ROOT_UID); + } + + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(userId, false); + } + final long identity = Binder.clearCallingIdentity(); + try { + userState.setBindInstantServiceAllowed(allowed); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + private boolean isPrintingEnabled() { return !mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING, Binder.getCallingUserHandle()); @@ -773,7 +826,7 @@ public final class PrintManagerService extends SystemService { List installedServices = mContext.getPackageManager() .queryIntentServicesAsUser(intent, - GET_SERVICES | MATCH_DEBUG_TRIAGED_MISSING, + GET_SERVICES | MATCH_DEBUG_TRIAGED_MISSING | MATCH_INSTANT, getChangingUserId()); return installedServices != null && !installedServices.isEmpty(); @@ -988,7 +1041,7 @@ public final class PrintManagerService extends SystemService { return appId; } final int callingAppId = UserHandle.getAppId(callingUid); - if (appId == callingAppId || callingAppId == Process.SHELL_UID + if (appId == callingAppId || callingAppId == SHELL_UID || callingAppId == Process.SYSTEM_UID) { return appId; } diff --git a/services/print/java/com/android/server/print/PrintShellCommand.java b/services/print/java/com/android/server/print/PrintShellCommand.java new file mode 100644 index 0000000000000..11642e5a45e91 --- /dev/null +++ b/services/print/java/com/android/server/print/PrintShellCommand.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.print; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.RemoteException; +import android.os.ShellCommand; +import android.os.UserHandle; +import android.print.IPrintManager; + +import java.io.PrintWriter; + +/** + * Shell command implementation for the print manager service + */ +final class PrintShellCommand extends ShellCommand { + final @NonNull IPrintManager mService; + + PrintShellCommand(@NonNull IPrintManager service) { + mService = service; + } + + @Override + public int onCommand(@Nullable String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + switch (cmd) { + case "get-bind-instant-service-allowed": { + return runGetBindInstantServiceAllowed(); + } + case "set-bind-instant-service-allowed": { + return runSetBindInstantServiceAllowed(); + } + } + return -1; + } + + private int runGetBindInstantServiceAllowed() { + final Integer userId = parseUserId(); + if (userId == null) { + return -1; + } + try { + getOutPrintWriter().println( + Boolean.toString(mService.getBindInstantServiceAllowed(userId))); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return 0; + } + + private int runSetBindInstantServiceAllowed() { + final Integer userId = parseUserId(); + if (userId == null) { + return -1; + } + final String allowed = getNextArgRequired(); + if (allowed == null) { + getErrPrintWriter().println("Error: no true/false specified"); + return -1; + } + try { + mService.setBindInstantServiceAllowed(userId, Boolean.parseBoolean(allowed)); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return 0; + } + + private @Nullable Integer parseUserId() { + final String option = getNextOption(); + if (option != null) { + if (option.equals("--user")) { + return UserHandle.parseUserArg(getNextArgRequired()); + } else { + getErrPrintWriter().println("Unknown option: " + option); + return null; + } + } + return UserHandle.USER_SYSTEM; + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + pw.println("Print service commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" set-bind-instant-service-allowed [--user ] true|false "); + pw.println(" Set whether binding to print services provided by instant apps is " + + "allowed."); + pw.println(" get-bind-instant-service-allowed [--user ]"); + pw.println(" Get whether binding to print services provided by instant apps is " + + "allowed."); + } +} diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java index d4cbe7b1cf66e..4f0d6f1d55357 100644 --- a/services/print/java/com/android/server/print/RemotePrintService.java +++ b/services/print/java/com/android/server/print/RemotePrintService.java @@ -571,8 +571,8 @@ final class RemotePrintService implements DeathRecipient { mBinding = true; boolean wasBound = mContext.bindServiceAsUser(mIntent, mServiceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, - new UserHandle(mUserId)); + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE + | Context.BIND_ALLOW_INSTANT, new UserHandle(mUserId)); if (!wasBound) { if (DEBUG) { diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index 62185d782cbc1..4fbc14c0097f2 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -19,6 +19,7 @@ package com.android.server.print; import static android.content.pm.PackageManager.GET_META_DATA; import static android.content.pm.PackageManager.GET_SERVICES; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; +import static android.content.pm.PackageManager.MATCH_INSTANT; import static com.android.internal.print.DumpUtils.writePrintJobInfo; import static com.android.internal.print.DumpUtils.writePrinterId; @@ -155,6 +156,11 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, */ private RemotePrintServiceRecommendationService mPrintServiceRecommendationsService; + /** + * Can services from instant apps be bound? (usually disabled, only used by testing) + */ + private boolean mIsInstantServiceAllowed; + public UserState(Context context, int userId, Object lock, boolean lowPriority) { mContext = context; mUserId = userId; @@ -872,9 +878,14 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, private void readInstalledPrintServicesLocked() { Set tempPrintServices = new HashSet(); + int queryIntentFlags = GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING; + + if (mIsInstantServiceAllowed) { + queryIntentFlags |= MATCH_INSTANT; + } + List installedServices = mContext.getPackageManager() - .queryIntentServicesAsUser(mQueryIntent, - GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING, mUserId); + .queryIntentServicesAsUser(mQueryIntent, queryIntentFlags, mUserId); final int installedCount = installedServices.size(); for (int i = 0, count = installedCount; i < count; i++) { @@ -1185,6 +1196,18 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, } } + public boolean getBindInstantServiceAllowed() { + return mIsInstantServiceAllowed; + } + + public void setBindInstantServiceAllowed(boolean allowed) { + synchronized (mLock) { + mIsInstantServiceAllowed = allowed; + + updateIfNeededLocked(); + } + } + private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient { @NonNull final IPrintJobStateChangeListener listener; final int appId;