Merge "Fix flicker when releasing drag of divider"
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index eb7c712..031bdbd 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -28,13 +28,24 @@
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.pm.VerificationParams;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -45,13 +56,26 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
+import libcore.io.IoUtils;
+
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.SizedInputStream;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
public final class Pm {
private static final String TAG = "Pm";
@@ -82,7 +106,7 @@
System.exit(exitCode);
}
- public int run(String[] args) throws RemoteException {
+ public int run(String[] args) throws IOException, RemoteException {
boolean validCommand = false;
if (args.length < 1) {
return showUsage();
@@ -118,19 +142,19 @@
}
if ("install-create".equals(op)) {
- return runInstall();
+ return runInstallCreate();
}
if ("install-write".equals(op)) {
- return runInstall();
+ return runInstallWrite();
}
if ("install-commit".equals(op)) {
- return runInstall();
+ return runInstallCommit();
}
if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
- return runInstall();
+ return runInstallAbandon();
}
if ("set-installer".equals(op)) {
@@ -275,10 +299,6 @@
return -1;
}
- private int runInstall() {
- return runShellCommand("package", mArgs);
- }
-
/**
* Execute the list sub-command.
*
@@ -297,10 +317,6 @@
return runShellCommand("package", mArgs);
}
- private int runUninstall() {
- return runShellCommand("package", mArgs);
- }
-
private int runPath() {
int userId = UserHandle.USER_SYSTEM;
String option = nextOption();
@@ -354,6 +370,49 @@
}
}
+ /**
+ * Converts a failure code into a string by using reflection to find a matching constant
+ * in PackageManager.
+ */
+ private String installFailureToString(LocalPackageInstallObserver obs) {
+ final int result = obs.result;
+ Field[] fields = PackageManager.class.getFields();
+ for (Field f: fields) {
+ if (f.getType() == int.class) {
+ int modifiers = f.getModifiers();
+ // only look at public final static fields.
+ if (((modifiers & Modifier.FINAL) != 0) &&
+ ((modifiers & Modifier.PUBLIC) != 0) &&
+ ((modifiers & Modifier.STATIC) != 0)) {
+ String fieldName = f.getName();
+ if (fieldName.startsWith("INSTALL_FAILED_") ||
+ fieldName.startsWith("INSTALL_PARSE_FAILED_")) {
+ // get the int value and compare it to result.
+ try {
+ if (result == f.getInt(null)) {
+ StringBuilder sb = new StringBuilder(64);
+ sb.append(fieldName);
+ if (obs.extraPermission != null) {
+ sb.append(" perm=");
+ sb.append(obs.extraPermission);
+ }
+ if (obs.extraPackage != null) {
+ sb.append(" pkg=" + obs.extraPackage);
+ }
+ return sb.toString();
+ }
+ } catch (IllegalAccessException e) {
+ // this shouldn't happen since we only look for public static fields.
+ }
+ }
+ }
+ }
+ }
+
+ // couldn't find a matching constant? return the value
+ return Integer.toString(result);
+ }
+
// pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined}
private int runSetAppLink() {
int userId = UserHandle.USER_SYSTEM;
@@ -543,6 +602,316 @@
}
}
+ private int runInstall() {
+ int installFlags = 0;
+ int userId = UserHandle.USER_ALL;
+ String installerPackageName = null;
+
+ String opt;
+
+ String originatingUriString = null;
+ String referrer = null;
+ String abi = null;
+
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("-l")) {
+ installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+ } else if (opt.equals("-r")) {
+ installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ } else if (opt.equals("-i")) {
+ installerPackageName = nextOptionData();
+ if (installerPackageName == null) {
+ System.err.println("Error: no value specified for -i");
+ return 1;
+ }
+ } else if (opt.equals("-t")) {
+ installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+ } else if (opt.equals("-s")) {
+ // Override if -s option is specified.
+ installFlags |= PackageManager.INSTALL_EXTERNAL;
+ } else if (opt.equals("-f")) {
+ // Override if -s option is specified.
+ installFlags |= PackageManager.INSTALL_INTERNAL;
+ } else if (opt.equals("-d")) {
+ installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ } else if (opt.equals("-g")) {
+ installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
+ } else if (opt.equals("--originating-uri")) {
+ originatingUriString = nextOptionData();
+ if (originatingUriString == null) {
+ System.err.println("Error: must supply argument for --originating-uri");
+ return 1;
+ }
+ } else if (opt.equals("--referrer")) {
+ referrer = nextOptionData();
+ if (referrer == null) {
+ System.err.println("Error: must supply argument for --referrer");
+ return 1;
+ }
+ } else if (opt.equals("--abi")) {
+ abi = checkAbiArgument(nextOptionData());
+ } else if (opt.equals("--user")) {
+ userId = Integer.parseInt(nextOptionData());
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ userId = translateUserId(userId, "runInstall");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ installFlags |= PackageManager.INSTALL_ALL_USERS;
+ }
+
+ final Uri verificationURI;
+ final Uri originatingURI;
+ final Uri referrerURI;
+
+ if (originatingUriString != null) {
+ originatingURI = Uri.parse(originatingUriString);
+ } else {
+ originatingURI = null;
+ }
+
+ if (referrer != null) {
+ referrerURI = Uri.parse(referrer);
+ } else {
+ referrerURI = null;
+ }
+
+ // Populate apkURI, must be present
+ final String apkFilePath = nextArg();
+ System.err.println("\tpkg: " + apkFilePath);
+ if (apkFilePath == null) {
+ System.err.println("Error: no package specified");
+ return 1;
+ }
+
+ // Populate verificationURI, optionally present
+ final String verificationFilePath = nextArg();
+ if (verificationFilePath != null) {
+ System.err.println("\tver: " + verificationFilePath);
+ verificationURI = Uri.fromFile(new File(verificationFilePath));
+ } else {
+ verificationURI = null;
+ }
+
+ LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
+ try {
+ VerificationParams verificationParams = new VerificationParams(verificationURI,
+ originatingURI, referrerURI, VerificationParams.NO_UID, null);
+
+ mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
+ installerPackageName, verificationParams, abi, userId);
+
+ synchronized (obs) {
+ while (!obs.finished) {
+ try {
+ obs.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
+ System.out.println("Success");
+ return 0;
+ } else {
+ System.err.println("Failure ["
+ + installFailureToString(obs)
+ + "]");
+ return 1;
+ }
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ return 1;
+ }
+ }
+
+ /**
+ * @param userId The user id to be translated.
+ * @param logContext Optional human readable text to provide some context in error log.
+ * @return Translated concrete user id. This will include USER_ALL.
+ */
+ private int translateUserId(int userId, String logContext) {
+ return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, true, true, logContext, "pm command");
+ }
+
+ private int runInstallCreate() throws RemoteException {
+ int userId = UserHandle.USER_ALL;
+ String installerPackageName = null;
+
+ final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+
+ String opt;
+ while ((opt = nextOption()) != null) {
+ if (opt.equals("-l")) {
+ params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+ } else if (opt.equals("-r")) {
+ params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ } else if (opt.equals("-i")) {
+ installerPackageName = nextArg();
+ if (installerPackageName == null) {
+ throw new IllegalArgumentException("Missing installer package");
+ }
+ } else if (opt.equals("-t")) {
+ params.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+ } else if (opt.equals("-s")) {
+ params.installFlags |= PackageManager.INSTALL_EXTERNAL;
+ } else if (opt.equals("-f")) {
+ params.installFlags |= PackageManager.INSTALL_INTERNAL;
+ } else if (opt.equals("-d")) {
+ params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ } else if (opt.equals("-g")) {
+ params.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
+ } else if (opt.equals("--originating-uri")) {
+ params.originatingUri = Uri.parse(nextOptionData());
+ } else if (opt.equals("--referrer")) {
+ params.referrerUri = Uri.parse(nextOptionData());
+ } else if (opt.equals("-p")) {
+ params.mode = SessionParams.MODE_INHERIT_EXISTING;
+ params.appPackageName = nextOptionData();
+ if (params.appPackageName == null) {
+ throw new IllegalArgumentException("Missing inherit package name");
+ }
+ } else if (opt.equals("-S")) {
+ params.setSize(Long.parseLong(nextOptionData()));
+ } else if (opt.equals("--abi")) {
+ params.abiOverride = checkAbiArgument(nextOptionData());
+ } else if (opt.equals("--user")) {
+ userId = Integer.parseInt(nextOptionData());
+ } else if (opt.equals("--install-location")) {
+ params.installLocation = Integer.parseInt(nextOptionData());
+ } else if (opt.equals("--force-uuid")) {
+ params.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
+ params.volumeUuid = nextOptionData();
+ if ("internal".equals(params.volumeUuid)) {
+ params.volumeUuid = null;
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown option " + opt);
+ }
+ }
+
+ userId = translateUserId(userId, "runInstallCreate");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ params.installFlags |= PackageManager.INSTALL_ALL_USERS;
+ }
+
+ final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
+
+ // NOTE: adb depends on parsing this string
+ System.out.println("Success: created install session [" + sessionId + "]");
+ return 0;
+ }
+
+ private int runInstallWrite() throws IOException, RemoteException {
+ long sizeBytes = -1;
+
+ String opt;
+ while ((opt = nextOption()) != null) {
+ if (opt.equals("-S")) {
+ sizeBytes = Long.parseLong(nextOptionData());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+
+ final int sessionId = Integer.parseInt(nextArg());
+ final String splitName = nextArg();
+
+ String path = nextArg();
+ if ("-".equals(path)) {
+ path = null;
+ } else if (path != null) {
+ final File file = new File(path);
+ if (file.isFile()) {
+ sizeBytes = file.length();
+ }
+ }
+
+ final SessionInfo info = mInstaller.getSessionInfo(sessionId);
+
+ PackageInstaller.Session session = null;
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+
+ if (path != null) {
+ in = new FileInputStream(path);
+ } else {
+ in = new SizedInputStream(System.in, sizeBytes);
+ }
+ out = session.openWrite(splitName, 0, sizeBytes);
+
+ int total = 0;
+ byte[] buffer = new byte[65536];
+ int c;
+ while ((c = in.read(buffer)) != -1) {
+ total += c;
+ out.write(buffer, 0, c);
+
+ if (info.sizeBytes > 0) {
+ final float fraction = ((float) c / (float) info.sizeBytes);
+ session.addProgress(fraction);
+ }
+ }
+ session.fsync(out);
+
+ System.out.println("Success: streamed " + total + " bytes");
+ return 0;
+ } finally {
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private int runInstallCommit() throws RemoteException {
+ final int sessionId = Integer.parseInt(nextArg());
+
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ session.commit(receiver.getIntentSender());
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ System.out.println("Success");
+ return 0;
+ } else {
+ Log.e(TAG, "Failure details: " + result.getExtras());
+ System.err.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ return 1;
+ }
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private int runInstallAbandon() throws RemoteException {
+ final int sessionId = Integer.parseInt(nextArg());
+
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+ session.abandon();
+ System.out.println("Success");
+ return 0;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
private int runSetInstaller() throws RemoteException {
final String targetPackage = nextArg();
final String installerPackageName = nextArg();
@@ -711,6 +1080,80 @@
}
}
+ private int runUninstall() throws RemoteException {
+ int flags = 0;
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("-k")) {
+ flags |= PackageManager.DELETE_KEEP_DATA;
+ } else if (opt.equals("--user")) {
+ String param = nextArg();
+ if (isNumber(param)) {
+ userId = Integer.parseInt(param);
+ } else {
+ showUsage();
+ System.err.println("Error: Invalid user: " + param);
+ return 1;
+ }
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ String pkg = nextArg();
+ if (pkg == null) {
+ System.err.println("Error: no package specified");
+ return showUsage();
+ }
+
+ userId = translateUserId(userId, "runUninstall");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ flags |= PackageManager.DELETE_ALL_USERS;
+ } else {
+ PackageInfo info;
+ try {
+ info = mPm.getPackageInfo(pkg, 0, userId);
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ return 1;
+ }
+ if (info == null) {
+ System.err.println("Failure - not installed for " + userId);
+ return 1;
+ }
+ final boolean isSystem =
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ // If we are being asked to delete a system app for just one
+ // user set flag so it disables rather than reverting to system
+ // version of the app.
+ if (isSystem) {
+ flags |= PackageManager.DELETE_SYSTEM_APP;
+ }
+ }
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ mInstaller.uninstall(pkg, null /* callerPackageName */, flags,
+ receiver.getIntentSender(), userId);
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ System.out.println("Success");
+ return 0;
+ } else {
+ Log.e(TAG, "Failure details: " + result.getExtras());
+ System.err.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ return 1;
+ }
+ }
+
static class ClearDataObserver extends IPackageDataObserver.Stub {
boolean finished;
boolean result;
@@ -1056,6 +1499,54 @@
return 1;
}
+ private static String checkAbiArgument(String abi) {
+ if (TextUtils.isEmpty(abi)) {
+ throw new IllegalArgumentException("Missing ABI argument");
+ }
+
+ if ("-".equals(abi)) {
+ return abi;
+ }
+
+ final String[] supportedAbis = Build.SUPPORTED_ABIS;
+ for (String supportedAbi : supportedAbis) {
+ if (supportedAbi.equals(abi)) {
+ return abi;
+ }
+ }
+
+ throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
+ }
+
+ private static class LocalIntentReceiver {
+ private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
+
+ private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public int send(int code, Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ try {
+ mResult.offer(intent, 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return 0;
+ }
+ };
+
+ public IntentSender getIntentSender() {
+ return new IntentSender((IIntentSender) mLocalSender);
+ }
+
+ public Intent getResult() {
+ try {
+ return mResult.take();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
private String nextOption() {
if (mNextArg >= mArgs.length) {
return null;
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index cad482b..73c2c80 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -19,11 +19,8 @@
import android.util.Slog;
import com.android.internal.util.FastPrintWriter;
-import java.io.BufferedInputStream;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.InputStream;
import java.io.PrintWriter;
/**
@@ -46,7 +43,6 @@
private FastPrintWriter mOutPrintWriter;
private FastPrintWriter mErrPrintWriter;
- private InputStream mInputStream;
public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) {
@@ -115,13 +111,6 @@
return mErrPrintWriter;
}
- public InputStream getInputStream() {
- if (mInputStream == null) {
- mInputStream = new BufferedInputStream(new FileInputStream(mIn));
- }
- return mInputStream;
- }
-
/**
* Return the next option on the command line -- that is an argument that
* starts with '-'. If the next argument is not an option, null is returned.
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index 7b2cf25..db2bf1a 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -46,4 +46,5 @@
void writePrintJobData(in ParcelFileDescriptor fd, in PrintJobId printJobId);
void setClient(IPrintSpoolerClient client);
void setPrintJobCancelling(in PrintJobId printJobId, boolean cancelling);
+ void removeApprovedPrintService(in ComponentName serviceToRemove);
}
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index e92f74c..50237832 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -187,6 +187,18 @@
<!-- Label for a printer that is not available. [CHAR LIMIT=25] -->
<string name="printer_unavailable"><xliff:g id="print_job_name" example="Canon-123GHT">%1$s</xliff:g> – unavailable</string>
+ <!-- Title for a warning message about security implications of using a print service,
+ displayed as a dialog message when the user prints using a print service that has not been
+ used before. [CHAR LIMIT=NONE] -->
+ <string name="print_service_security_warning_title">Use
+ <xliff:g id="service" example="My Print Service">%1$s</xliff:g>?</string>
+
+ <!-- Summary for a warning message about security implications of using a print service,
+ displayed as a dialog message when the user prints using a print service that has not been
+ used before. [CHAR LIMIT=NONE] -->
+ <string name="print_service_security_warning_summary">Your document may pass through one or
+ more servers on its way to the printer.</string>
+
<!-- Arrays -->
<!-- Color mode labels. -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index bafccae..7adcfec 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -50,6 +50,7 @@
import com.android.internal.os.HandlerCaller;
import com.android.internal.util.FastXmlSerializer;
import com.android.printspooler.R;
+import com.android.printspooler.util.ApprovedPrintServices;
import libcore.io.IoUtils;
@@ -67,6 +68,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* Service for exposing some of the {@link PrintSpooler} functionality to
@@ -136,10 +138,10 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- synchronized (mLock) {
- String prefix = (args.length > 0) ? args[0] : "";
- String tab = " ";
+ String prefix = (args.length > 0) ? args[0] : "";
+ String tab = " ";
+ synchronized (mLock) {
pw.append(prefix).append("print jobs:").println();
final int printJobCount = mPrintJobs.size();
for (int i = 0; i < printJobCount; i++) {
@@ -160,6 +162,14 @@
}
}
}
+
+ pw.append(prefix).append("approved print services:").println();
+ Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
+ if (approvedPrintServices != null) {
+ for (String approvedService : approvedPrintServices) {
+ pw.append(prefix).append(tab).append(approvedService).println();
+ }
+ }
}
private void sendOnPrintJobQueued(PrintJobInfo printJob) {
@@ -1307,6 +1317,12 @@
PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling);
}
+ @Override
+ public void removeApprovedPrintService(ComponentName serviceToRemove) {
+ (new ApprovedPrintServices(PrintSpoolerService.this))
+ .removeApprovedService(serviceToRemove);
+ }
+
public PrintSpoolerService getService() {
return PrintSpoolerService.this;
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index e8a5e43..4f80d91 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -17,14 +17,21 @@
package com.android.printspooler.ui;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -81,6 +88,7 @@
import com.android.printspooler.model.RemotePrintDocument.RemotePrintDocumentInfo;
import com.android.printspooler.renderer.IPdfEditor;
import com.android.printspooler.renderer.PdfManipulationService;
+import com.android.printspooler.util.ApprovedPrintServices;
import com.android.printspooler.util.MediaSizeUtils;
import com.android.printspooler.util.MediaSizeUtils.MediaSizeComparator;
import com.android.printspooler.util.PageRangeUtils;
@@ -88,6 +96,7 @@
import com.android.printspooler.widget.PrintContentView;
import com.android.printspooler.widget.PrintContentView.OptionsStateChangeListener;
import com.android.printspooler.widget.PrintContentView.OptionsStateController;
+
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -1184,12 +1193,125 @@
mPrintButton.setOnClickListener(clickListener);
}
+ /**
+ * A dialog that asks the user to approve a {@link PrintService}. This dialog is automatically
+ * dismissed if the same {@link PrintService} gets approved by another
+ * {@link PrintServiceApprovalDialog}.
+ */
+ private static final class PrintServiceApprovalDialog extends DialogFragment
+ implements OnSharedPreferenceChangeListener {
+ private static final String PRINTSERVICE_KEY = "PRINTSERVICE";
+ private ApprovedPrintServices mApprovedServices;
+
+ /**
+ * Create a new {@link PrintServiceApprovalDialog} that ask the user to approve a
+ * {@link PrintService}.
+ *
+ * @param printService The {@link ComponentName} of the service to approve
+ * @return A new {@link PrintServiceApprovalDialog} that might approve the service
+ */
+ static PrintServiceApprovalDialog newInstance(ComponentName printService) {
+ PrintServiceApprovalDialog dialog = new PrintServiceApprovalDialog();
+
+ Bundle args = new Bundle();
+ args.putParcelable(PRINTSERVICE_KEY, printService);
+ dialog.setArguments(args);
+
+ return dialog;
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ mApprovedServices.unregisterChangeListener(this);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);
+ synchronized (ApprovedPrintServices.sLock) {
+ if (mApprovedServices.isApprovedService(printService)) {
+ dismiss();
+ } else {
+ mApprovedServices.registerChangeListenerLocked(this);
+ }
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+
+ mApprovedServices = new ApprovedPrintServices(getActivity());
+
+ PackageManager packageManager = getActivity().getPackageManager();
+ CharSequence serviceLabel;
+ try {
+ ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);
+
+ serviceLabel = packageManager.getApplicationInfo(printService.getPackageName(), 0)
+ .loadLabel(packageManager);
+ } catch (NameNotFoundException e) {
+ serviceLabel = null;
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(getString(R.string.print_service_security_warning_title,
+ serviceLabel))
+ .setMessage(getString(R.string.print_service_security_warning_summary,
+ serviceLabel))
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ ComponentName printService =
+ getArguments().getParcelable(PRINTSERVICE_KEY);
+ // Prevent onSharedPreferenceChanged from getting triggered
+ mApprovedServices
+ .unregisterChangeListener(PrintServiceApprovalDialog.this);
+
+ mApprovedServices.addApprovedService(printService);
+ ((PrintActivity) getActivity()).confirmPrint();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null);
+
+ return builder.create();
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);
+
+ synchronized (ApprovedPrintServices.sLock) {
+ if (mApprovedServices.isApprovedService(printService)) {
+ dismiss();
+ }
+ }
+ }
+ }
+
private final class MyClickListener implements OnClickListener {
@Override
public void onClick(View view) {
if (view == mPrintButton) {
if (mCurrentPrinter != null) {
- confirmPrint();
+ if (mDestinationSpinnerAdapter.getPdfPrinter() == mCurrentPrinter) {
+ confirmPrint();
+ } else {
+ ApprovedPrintServices approvedServices =
+ new ApprovedPrintServices(PrintActivity.this);
+
+ ComponentName printService = mCurrentPrinter.getId().getServiceName();
+ if (approvedServices.isApprovedService(printService)) {
+ confirmPrint();
+ } else {
+ PrintServiceApprovalDialog.newInstance(printService)
+ .show(getFragmentManager(), "approve");
+ }
+ }
} else {
cancelPrint();
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java b/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
new file mode 100644
index 0000000..dd10567
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 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.printspooler.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.printservice.PrintService;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/**
+ * Manage approved print services. These services are stored in the shared preferences.
+ */
+public class ApprovedPrintServices {
+ /**
+ * Used for locking accesses to the approved services.
+ */
+ static final public Object sLock = new Object();
+
+ private static final String APPROVED_SERVICES_PREFERENCE = "PRINT_SPOOLER_APPROVED_SERVICES";
+ private final SharedPreferences mPreferences;
+
+ /**
+ * Create a new {@link ApprovedPrintServices}
+ *
+ * @param owner The {@link Context} using this object.
+ */
+ public ApprovedPrintServices(Context owner) {
+ mPreferences = owner.getSharedPreferences(APPROVED_SERVICES_PREFERENCE,
+ Context.MODE_PRIVATE);
+ }
+
+ /**
+ * Get {@link Set} of approved services.
+ *
+ * @return A {@link Set} containing all currently approved services.
+ */
+ public Set<String> getApprovedServices() {
+ return mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null);
+ }
+
+ /**
+ * Check if a {@link PrintService} is approved.
+ *
+ * This function does not acquire the {@link #sLock}.
+ *
+ * @param service The {@link ComponentName} of the {@link PrintService} that might be approved
+ * @return true iff the service is currently approved
+ */
+ public boolean isApprovedService(ComponentName service) {
+ final Set<String> approvedServices = getApprovedServices();
+
+ if (approvedServices != null) {
+ final String flattenedString = service.flattenToShortString();
+
+ for (String approvedService : approvedServices) {
+ if (approvedService.equals(flattenedString)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Add a {@link PrintService} to the list of approved print services.
+ *
+ * @param serviceToAdd The {@link ComponentName} of the {@link PrintService} to be approved.
+ */
+ public void addApprovedService(ComponentName serviceToAdd) {
+ synchronized (sLock) {
+ Set<String> oldApprovedServices =
+ mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null);
+
+ Set<String> newApprovedServices;
+ if (oldApprovedServices == null) {
+ newApprovedServices = new ArraySet<String>(1);
+ } else {
+ // Copy approved services.
+ newApprovedServices = new ArraySet<String>(oldApprovedServices);
+ }
+ newApprovedServices.add(serviceToAdd.flattenToShortString());
+
+ SharedPreferences.Editor editor = mPreferences.edit();
+ editor.putStringSet(APPROVED_SERVICES_PREFERENCE, newApprovedServices);
+ editor.apply();
+ }
+ }
+
+ /**
+ * Add a {@link OnSharedPreferenceChangeListener} that listens for changes to the approved
+ * services. Should only be called while holding {@link #sLock} to synchronize against
+ * {@link #addApprovedService}.
+ *
+ * @param listener {@link OnSharedPreferenceChangeListener} to register
+ */
+ public void registerChangeListenerLocked(OnSharedPreferenceChangeListener listener) {
+ mPreferences.registerOnSharedPreferenceChangeListener(listener);
+ }
+
+ /**
+ * Unregister a listener registered in {@link #registerChangeListenerLocked}.
+ *
+ * @param listener {@link OnSharedPreferenceChangeListener} to unregister
+ */
+ public void unregisterChangeListener(OnSharedPreferenceChangeListener listener) {
+ mPreferences.unregisterOnSharedPreferenceChangeListener(listener);
+ }
+
+ /**
+ * If a {@link PrintService} is approved, remove it from the list of approved services.
+ *
+ * @param serviceToRemove The {@link ComponentName} of the {@link PrintService} to be removed
+ */
+ public void removeApprovedService(ComponentName serviceToRemove) {
+ synchronized (sLock) {
+ if (isApprovedService(serviceToRemove)) {
+ // Copy approved services.
+ ArraySet<String> approvedServices = new ArraySet<String>(
+ mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null));
+
+ SharedPreferences.Editor editor = mPreferences.edit();
+
+ final int numApprovedServices = approvedServices.size();
+ for (int i = 0; i < numApprovedServices; i++) {
+ if (approvedServices.valueAt(i)
+ .equals(serviceToRemove.flattenToShortString())) {
+ approvedServices.removeAt(i);
+ break;
+ }
+ }
+
+ editor.putStringSet(APPROVED_SERVICES_PREFERENCE, approvedServices);
+ editor.apply();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d7176fd..c259ac2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1,68 +1,28 @@
-/*
- * Copyright (C) 2015 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.pm;
-import android.app.ActivityManager;
import android.content.ComponentName;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionParams;
import android.content.res.AssetManager;
import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
-import android.text.TextUtils;
-import com.android.internal.util.SizedInputStream;
-
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.WeakHashMap;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.TimeUnit;
class PackageManagerShellCommand extends ShellCommand {
final IPackageManager mInterface;
@@ -82,21 +42,8 @@
final PrintWriter pw = getOutPrintWriter();
try {
switch(cmd) {
- case "install":
- return runInstall();
- case "install-abandon":
- case "install-destroy":
- return runInstallAbandon();
- case "install-commit":
- return runInstallCommit();
- case "install-create":
- return runInstallCreate();
- case "install-write":
- return runInstallWrite();
case "list":
return runList();
- case "uninstall":
- return runUninstall();
default:
return handleDefaultCommands(cmd);
}
@@ -106,65 +53,6 @@
return -1;
}
- private int runInstall() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- final InstallParams params = makeInstallParams();
- final int sessionId = doCreateSession(params.sessionParams,
- params.installerPackageName, params.userId);
-
- final String inPath = getNextArg();
- if (inPath == null && params.sessionParams.sizeBytes == 0) {
- pw.println("Error: must either specify a package size or an APK file");
- return 1;
- }
- if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk") != 0) {
- return 1;
- }
- if (doCommitSession(sessionId) != 0) {
- return 1;
- }
- return 0;
- }
-
- private int runInstallAbandon() throws RemoteException {
- final int sessionId = Integer.parseInt(getNextArg());
- return doAbandonSession(sessionId);
- }
-
- private int runInstallCommit() throws RemoteException {
- final int sessionId = Integer.parseInt(getNextArg());
- return doCommitSession(sessionId);
- }
-
- private int runInstallCreate() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- final InstallParams installParams = makeInstallParams();
- final int sessionId = doCreateSession(installParams.sessionParams,
- installParams.installerPackageName, installParams.userId);
-
- // NOTE: adb depends on parsing this string
- pw.println("Success: created install session [" + sessionId + "]");
- return 0;
- }
-
- private int runInstallWrite() throws RemoteException {
- long sizeBytes = -1;
-
- String opt;
- while ((opt = getNextOption()) != null) {
- if (opt.equals("-S")) {
- sizeBytes = Long.parseLong(getNextArg());
- } else {
- throw new IllegalArgumentException("Unknown option: " + opt);
- }
- }
-
- final int sessionId = Integer.parseInt(getNextArg());
- final String splitName = getNextArg();
- final String path = getNextArg();
- return doWriteSession(sessionId, path, sizeBytes, splitName);
- }
-
private int runList() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final String type = getNextArg();
@@ -475,279 +363,6 @@
return 0;
}
- private int runUninstall() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- int flags = 0;
- int userId = UserHandle.USER_ALL;
-
- String opt;
- while ((opt = getNextOption()) != null) {
- switch (opt) {
- case "-k":
- flags |= PackageManager.DELETE_KEEP_DATA;
- break;
- case "--user":
- userId = Integer.parseInt(getNextArg());
- break;
- default:
- pw.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- String packageName = getNextArg();
- if (packageName == null) {
- pw.println("Error: package name not specified");
- return 1;
- }
-
- userId = translateUserId(userId, "runUninstall");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- flags |= PackageManager.DELETE_ALL_USERS;
- } else {
- final PackageInfo info = mInterface.getPackageInfo(packageName, 0, userId);
- if (info == null) {
- pw.println("Failure - not installed for " + userId);
- return 1;
- }
- final boolean isSystem =
- (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- // If we are being asked to delete a system app for just one
- // user set flag so it disables rather than reverting to system
- // version of the app.
- if (isSystem) {
- flags |= PackageManager.DELETE_SYSTEM_APP;
- }
- }
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- mInterface.getPackageInstaller().uninstall(packageName, null /*callerPackageName*/, flags,
- receiver.getIntentSender(), userId);
-
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- pw.println("Success");
- return 0;
- } else {
- pw.println("Failure ["
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
- return 1;
- }
- }
-
- private static class InstallParams {
- SessionParams sessionParams;
- String installerPackageName;
- int userId = UserHandle.USER_ALL;
- }
-
- private InstallParams makeInstallParams() {
- final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
- final InstallParams params = new InstallParams();
- params.sessionParams = sessionParams;
- String opt;
- while ((opt = getNextOption()) != null) {
- switch (opt) {
- case "-l":
- sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- break;
- case "-r":
- sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- break;
- case "-i":
- params.installerPackageName = getNextArg();
- if (params.installerPackageName == null) {
- throw new IllegalArgumentException("Missing installer package");
- }
- break;
- case "-t":
- sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
- break;
- case "-s":
- sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
- break;
- case "-f":
- sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
- break;
- case "-d":
- sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
- break;
- case "-g":
- sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
- break;
- case "--originating-uri":
- sessionParams.originatingUri = Uri.parse(getNextArg());
- break;
- case "--referrer":
- sessionParams.referrerUri = Uri.parse(getNextArg());
- break;
- case "-p":
- sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING;
- sessionParams.appPackageName = getNextArg();
- if (sessionParams.appPackageName == null) {
- throw new IllegalArgumentException("Missing inherit package name");
- }
- break;
- case "-S":
- sessionParams.setSize(Long.parseLong(getNextArg()));
- break;
- case "--abi":
- sessionParams.abiOverride = checkAbiArgument(getNextArg());
- break;
- case "--user":
- params.userId = Integer.parseInt(getNextArg());
- break;
- case "--install-location":
- sessionParams.installLocation = Integer.parseInt(getNextArg());
- break;
- case "--force-uuid":
- sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
- sessionParams.volumeUuid = getNextArg();
- if ("internal".equals(sessionParams.volumeUuid)) {
- sessionParams.volumeUuid = null;
- }
- break;
- default:
- throw new IllegalArgumentException("Unknown option " + opt);
- }
- }
- return params;
- }
-
- private static String checkAbiArgument(String abi) {
- if (TextUtils.isEmpty(abi)) {
- throw new IllegalArgumentException("Missing ABI argument");
- }
-
- if ("-".equals(abi)) {
- return abi;
- }
-
- final String[] supportedAbis = Build.SUPPORTED_ABIS;
- for (String supportedAbi : supportedAbis) {
- if (supportedAbi.equals(abi)) {
- return abi;
- }
- }
-
- throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
- }
-
- private int translateUserId(int userId, String logContext) {
- return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, true, true, logContext, "pm command");
- }
-
- private int doCreateSession(SessionParams params, String installerPackageName, int userId)
- throws RemoteException {
- userId = translateUserId(userId, "runInstallCreate");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- params.installFlags |= PackageManager.INSTALL_ALL_USERS;
- }
-
- final int sessionId = mInterface.getPackageInstaller()
- .createSession(params, installerPackageName, userId);
- return sessionId;
- }
-
- private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName)
- throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- if ("-".equals(inPath)) {
- inPath = null;
- } else if (inPath != null) {
- final File file = new File(inPath);
- if (file.isFile()) {
- sizeBytes = file.length();
- }
- }
-
- final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId);
-
- PackageInstaller.Session session = null;
- InputStream in = null;
- OutputStream out = null;
- try {
- session = new PackageInstaller.Session(
- mInterface.getPackageInstaller().openSession(sessionId));
-
- if (inPath != null) {
- in = new FileInputStream(inPath);
- } else {
- in = new SizedInputStream(getInputStream(), sizeBytes);
- }
- out = session.openWrite(splitName, 0, sizeBytes);
-
- int total = 0;
- byte[] buffer = new byte[65536];
- int c;
- while ((c = in.read(buffer)) != -1) {
- total += c;
- out.write(buffer, 0, c);
-
- if (info.sizeBytes > 0) {
- final float fraction = ((float) c / (float) info.sizeBytes);
- session.addProgress(fraction);
- }
- }
- session.fsync(out);
-
- pw.println("Success: streamed " + total + " bytes");
- return 0;
- } catch (IOException e) {
- pw.println("Error: failed to write; " + e.getMessage());
- return 1;
- } finally {
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(session);
- }
- }
-
- private int doCommitSession(int sessionId) throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- PackageInstaller.Session session = null;
- try {
- session = new PackageInstaller.Session(
- mInterface.getPackageInstaller().openSession(sessionId));
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- session.commit(receiver.getIntentSender());
-
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- pw.println("Success");
- } else {
- pw.println("Failure ["
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
- pw.println("Failure details: " + result.getExtras());
- }
- return status;
- } finally {
- IoUtils.closeQuietly(session);
- }
- }
-
- private int doAbandonSession(int sessionId) throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- PackageInstaller.Session session = null;
- try {
- session = new PackageInstaller.Session(
- mInterface.getPackageInstaller().openSession(sessionId));
- session.abandon();
- pw.println("Success");
- return 0;
- } finally {
- IoUtils.closeQuietly(session);
- }
- }
-
private void doListPermissions(ArrayList<String> groupList, boolean groups, boolean labels,
boolean summary, int startProtectionLevel, int endProtectionLevel)
throws RemoteException {
@@ -910,34 +525,5 @@
pw.println(" -u: list only the permissions users will see");
pw.println("");
}
-
- private static class LocalIntentReceiver {
- private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
-
- private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
- @Override
- public int send(int code, Intent intent, String resolvedType,
- IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- try {
- mResult.offer(intent, 5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- return 0;
- }
- };
-
- public IntentSender getIntentSender() {
- return new IntentSender((IIntentSender) mLocalSender);
- }
-
- public Intent getResult() {
- try {
- return mResult.take();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
}
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 34347cf..92bd81f 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -19,14 +19,10 @@
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -51,7 +47,6 @@
import android.text.TextUtils;
import android.util.SparseArray;
-import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.server.SystemService;
@@ -527,6 +522,7 @@
while (iterator.hasNext()) {
ComponentName componentName = iterator.next();
if (packageName.equals(componentName.getPackageName())) {
+ userState.removeApprovedPrintService(componentName);
iterator.remove();
servicesRemoved = true;
}
@@ -587,15 +583,29 @@
return;
}
- final int installedServiceCount = installedServices.size();
- for (int i = 0; i < installedServiceCount; i++) {
- ServiceInfo serviceInfo = installedServices.get(i).serviceInfo;
- ComponentName component = new ComponentName(serviceInfo.packageName,
- serviceInfo.name);
- String label = serviceInfo.loadLabel(mContext.getPackageManager())
- .toString();
- showEnableInstalledPrintServiceNotification(component, label,
- getChangingUserId());
+ // Enable all added services by default
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+
+ Set<ComponentName> enabledServices = userState.getEnabledServices();
+ boolean servicesAdded = false;
+
+ final int installedServiceCount = installedServices.size();
+ for (int i = 0; i < installedServiceCount; i++) {
+ ServiceInfo serviceInfo = installedServices.get(i).serviceInfo;
+ ComponentName component = new ComponentName(serviceInfo.packageName,
+ serviceInfo.name);
+
+ enabledServices.add(component);
+ servicesAdded = true;
+ }
+
+ if (servicesAdded) {
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_PRINT_SERVICES, enabledServices,
+ getChangingUserId());
+ userState.updateIfNeededLocked();
+ }
}
}
@@ -733,43 +743,5 @@
Binder.restoreCallingIdentity(identity);
}
}
-
- private void showEnableInstalledPrintServiceNotification(ComponentName component,
- String label, int userId) {
- UserHandle userHandle = new UserHandle(userId);
-
- Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
- intent.putExtra(EXTRA_PRINT_SERVICE_COMPONENT_NAME, component.flattenToString());
-
- PendingIntent pendingIntent = PendingIntent.getActivityAsUser(mContext, 0, intent,
- PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT, null,
- userHandle);
-
- Context builderContext = mContext;
- try {
- builderContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
- userHandle);
- } catch (NameNotFoundException e) {
- // Ignore can't find the package the system is running as.
- }
- Notification.Builder builder = new Notification.Builder(builderContext)
- .setSmallIcon(R.drawable.ic_print)
- .setContentTitle(mContext.getString(R.string.print_service_installed_title,
- label))
- .setContentText(mContext.getString(R.string.print_service_installed_message))
- .setContentIntent(pendingIntent)
- .setWhen(System.currentTimeMillis())
- .setAutoCancel(true)
- .setShowWhen(true)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
-
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- String notificationTag = getClass().getName() + ":" + component.flattenToString();
- notificationManager.notifyAsUser(notificationTag, 0, builder.build(),
- userHandle);
- }
}
}
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index 85c876a..e5370f4 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -279,6 +279,35 @@
}
}
+ /**
+ * Connect to the print spooler service and remove an approved print service.
+ *
+ * @param serviceToRemove The {@link ComponentName} of the service to be removed.
+ */
+ public final void removeApprovedPrintService(ComponentName serviceToRemove) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ mCanUnbind = false;
+ }
+ try {
+ getRemoteInstanceLazy().removeApprovedPrintService(serviceToRemove);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error removing approved print service.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error removing approved print service.", te);
+ } finally {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
+ + "] removing approved print service()");
+ }
+ synchronized (mLock) {
+ mCanUnbind = true;
+ mLock.notifyAll();
+ }
+ }
+ }
+
public final void removeObsoletePrintJobs() {
throwIfCalledOnMainThread();
synchronized (mLock) {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index ae19dac..f37bb9eb 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -297,6 +297,15 @@
}
}
+ /**
+ * Remove an approved print service.
+ *
+ * @param serviceToRemove The {@link ComponentName} of the service to be removed.
+ */
+ public void removeApprovedPrintService(ComponentName serviceToRemove) {
+ mSpooler.removeApprovedPrintService(serviceToRemove);
+ }
+
public void restartPrintJob(PrintJobId printJobId, int appId) {
PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId);
if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {