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
This commit is contained in:
Philip P. Moltmann
2018-05-10 15:22:12 -07:00
parent a1aafd7718
commit b3baaab4af
6 changed files with 213 additions and 6 deletions

View File

@@ -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);
}

View File

@@ -811,6 +811,8 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
List<ResolveInfo> resolvedActivities = getPackageManager()
.queryIntentActivities(intent, 0);
if (resolvedActivities.isEmpty()) {
Log.w(LOG_TAG, "Advanced options activity " + mAdvancedPrintOptionsActivity + " could "
+ "not be found");
return;
}

View File

@@ -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<ResolveInfo> 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;
}

View File

@@ -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 <USER_ID>] true|false ");
pw.println(" Set whether binding to print services provided by instant apps is "
+ "allowed.");
pw.println(" get-bind-instant-service-allowed [--user <USER_ID>]");
pw.println(" Get whether binding to print services provided by instant apps is "
+ "allowed.");
}
}

View File

@@ -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) {

View File

@@ -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<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
int queryIntentFlags = GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING;
if (mIsInstantServiceAllowed) {
queryIntentFlags |= MATCH_INSTANT;
}
List<ResolveInfo> 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;