Commit 7bfbbcb0 authored by Svetoslav's avatar Svetoslav Committed by Svetoslav Ganov

Refactor how the print dialog activity is started.

1. Before the print job activity was started asyncronously with
   respect to the print call on to the print manager. This was
   creating a situation where the starting activity may finish
   before the print dialog appears which may lead to an orphaned
   print document adapter with no data to print (as the UI is
   is gone), or strange behaviors where the print dialog starts
   on as a separate task.

   To address this the pending intent for starting the print
   dialog is not started by the print spooler since we cannot
   call into it synchronously as we have to start its process
   and bind to the spooler service which leads to jankyness in
   the client app. Now the pending intent is created by the
   print manager service in the synchronous print call so
   from an app's perspective calling print starts the activity.

   The side effect of this design is that the print dialog
   activity may start before the system is bound to the spooler
   service. In such a case the print activity cannot start
   poking the print spooler state as the system registers
   callback to observe the spooler state. To address this
   the print spooler activity disables the UI and also binds
   to the spooler service which happenes immediately after it
   is started. As soon as the print dialog binds to the
   service it starts the UI.

2. Fixed an bug in the printer adapter of the print dialog that
   was leading to a crash if the only item in the adater is the
   all pritners option and it is selected.

3. Piping the package name that started the printing so we can
   pass it to the storage UI as a hint to open the last location
   the app used.

bug:11127269

Change-Id: Ia93820bdae0b0e7600a0930b1f10d9708bd86b68
parent 1db8cf12
......@@ -165,7 +165,6 @@ LOCAL_SRC_FILES += \
core/java/android/print/ILayoutResultCallback.aidl \
core/java/android/print/IPrinterDiscoveryObserver.aidl \
core/java/android/print/IPrintDocumentAdapter.aidl \
core/java/android/print/IPrintClient.aidl \
core/java/android/print/IPrintJobStateChangeListener.aidl \
core/java/android/print/IPrintManager.aidl \
core/java/android/print/IPrintSpooler.aidl \
......
......@@ -183,6 +183,8 @@ $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/media/video/*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/effects/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/framework-res_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/print/IPrintClient.*)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
......@@ -16,9 +16,9 @@
package android.print;
import android.os.Bundle;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintClient;
import android.print.PrintJobId;
import android.print.IPrintJobStateChangeListener;
import android.print.PrinterId;
......@@ -34,9 +34,8 @@ import android.printservice.PrintServiceInfo;
interface IPrintManager {
List<PrintJobInfo> getPrintJobInfos(int appId, int userId);
PrintJobInfo getPrintJobInfo(in PrintJobId printJobId, int appId, int userId);
PrintJobInfo print(String printJobName, in IPrintClient client,
in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
int appId, int userId);
Bundle print(String printJobName, in IPrintDocumentAdapter printAdapter,
in PrintAttributes attributes, String packageName, int appId, int userId);
void cancelPrintJob(in PrintJobId printJobId, int appId, int userId);
void restartPrintJob(in PrintJobId printJobId, int appId, int userId);
......
......@@ -18,8 +18,6 @@ package android.print;
import android.content.ComponentName;
import android.os.ParcelFileDescriptor;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintClient;
import android.print.IPrintSpoolerClient;
import android.print.IPrintSpoolerCallbacks;
import android.print.PrinterInfo;
......@@ -40,8 +38,7 @@ oneway interface IPrintSpooler {
int state, int appId, int sequence);
void getPrintJobInfo(in PrintJobId printJobId, IPrintSpoolerCallbacks callback,
int appId, int sequence);
void createPrintJob(in PrintJobInfo printJob, in IPrintClient client,
in IPrintDocumentAdapter printAdapter);
void createPrintJob(in PrintJobInfo printJob);
void setPrintJobState(in PrintJobId printJobId, int status, String stateReason,
IPrintSpoolerCallbacks callback, int sequence);
void setPrintJobTag(in PrintJobId printJobId, String tag, IPrintSpoolerCallbacks callback,
......
......@@ -60,8 +60,47 @@ public final class PrintManager {
private static final boolean DEBUG = false;
private static final int MSG_START_PRINT_JOB_CONFIG_ACTIVITY = 1;
private static final int MSG_NOTIFY_PRINT_JOB_STATE_CHANGED = 2;
private static final int MSG_NOTIFY_PRINT_JOB_STATE_CHANGED = 1;
/**
* The action for launching the print dialog activity.
*
* @hide
*/
public static final String ACTION_PRINT_DIALOG = "android.print.PRINT_DIALOG";
/**
* Extra with the intent for starting the print dialog.
* <p>
* <strong>Type:</strong> {@link android.content.IntentSender}
* </p>
*
* @hide
*/
public static final String EXTRA_PRINT_DIALOG_INTENT =
"android.print.intent.extra.EXTRA_PRINT_DIALOG_INTENT";
/**
* Extra with a print job.
* <p>
* <strong>Type:</strong> {@link android.print.PrintJobInfo}
* </p>
*
* @hide
*/
public static final String EXTRA_PRINT_JOB =
"android.print.intent.extra.EXTRA_PRINT_JOB";
/**
* Extra with the print document adapter to be printed.
* <p>
* <strong>Type:</strong> {@link android.print.IPrintDocumentAdapter}
* </p>
*
* @hide
*/
public static final String EXTRA_PRINT_DOCUMENT_ADAPTER =
"android.print.intent.extra.EXTRA_PRINT_DOCUMENT_ADAPTER";
/** @hide */
public static final int APP_ID_ANY = -2;
......@@ -74,8 +113,6 @@ public final class PrintManager {
private final int mAppId;
private final PrintClient mPrintClient;
private final Handler mHandler;
private Map<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper> mPrintJobStateChangeListeners;
......@@ -103,24 +140,10 @@ public final class PrintManager {
mService = service;
mUserId = userId;
mAppId = appId;
mPrintClient = new PrintClient(this);
mHandler = new Handler(context.getMainLooper(), null, false) {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_START_PRINT_JOB_CONFIG_ACTIVITY: {
SomeArgs args = (SomeArgs) message.obj;
Context context = (Context) args.arg1;
IntentSender intent = (IntentSender) args.arg2;
args.recycle();
try {
context.startIntentSender(intent, null, 0, 0, 0);
} catch (SendIntentException sie) {
Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
}
}
break;
case MSG_NOTIFY_PRINT_JOB_STATE_CHANGED: {
SomeArgs args = (SomeArgs) message.obj;
PrintJobStateChangeListener listener =
......@@ -128,8 +151,7 @@ public final class PrintManager {
PrintJobId printJobId = (PrintJobId) args.arg2;
args.recycle();
listener.onPrintJobStateChanged(printJobId);
}
break;
} break;
}
}
};
......@@ -279,10 +301,20 @@ public final class PrintManager {
PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
mContext.getMainLooper());
try {
PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate,
attributes, mAppId, mUserId);
if (printJob != null) {
return new PrintJob(printJob, this);
Bundle result = mService.print(printJobName, delegate,
attributes, mContext.getPackageName(), mAppId, mUserId);
if (result != null) {
PrintJobInfo printJob = result.getParcelable(EXTRA_PRINT_JOB);
IntentSender intent = result.getParcelable(EXTRA_PRINT_DIALOG_INTENT);
if (printJob == null || intent == null) {
return null;
}
try {
mContext.startIntentSender(intent, null, 0, 0, 0);
return new PrintJob(printJob, this);
} catch (SendIntentException sie) {
Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
}
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error creating a print job", re);
......@@ -333,27 +365,6 @@ public final class PrintManager {
return new PrinterDiscoverySession(mService, mContext, mUserId);
}
private static final class PrintClient extends IPrintClient.Stub {
private final WeakReference<PrintManager> mWeakPrintManager;
public PrintClient(PrintManager manager) {
mWeakPrintManager = new WeakReference<PrintManager>(manager);
}
@Override
public void startPrintJobConfigActivity(IntentSender intent) {
PrintManager manager = mWeakPrintManager.get();
if (manager != null) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = manager.mContext;
args.arg2 = intent;
manager.mHandler.obtainMessage(MSG_START_PRINT_JOB_CONFIG_ACTIVITY,
args).sendToTarget();
}
}
}
private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
private final Object mLock = new Object();
......
......@@ -58,8 +58,13 @@
<activity
android:name=".PrintJobConfigActivity"
android:configChanges="orientation|screenSize"
android:exported="false"
android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE"
android:theme="@style/PrintJobConfigActivityTheme">
<intent-filter>
<action android:name="android.print.PRINT_DILAOG" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="printjob" android:pathPattern="*" />
</intent-filter>
</activity>
<activity
......
......@@ -31,10 +31,10 @@ import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.print.IPrintClient;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintJobStateChangeListener;
import android.print.IPrintManager;
......@@ -45,6 +45,7 @@ import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.SparseArray;
import com.android.internal.R;
......@@ -96,19 +97,19 @@ public final class PrintManagerService extends IPrintManager.Stub {
}
@Override
public PrintJobInfo print(String printJobName, final IPrintClient client,
final IPrintDocumentAdapter documentAdapter, PrintAttributes attributes,
int appId, int userId) {
public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
PrintAttributes attributes, String packageName, int appId, int userId) {
final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
String resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName);
final UserState userState;
synchronized (mLock) {
userState = getOrCreateUserStateLocked(resolvedUserId);
}
final long identity = Binder.clearCallingIdentity();
try {
return userState.print(printJobName, client, documentAdapter,
attributes, resolvedAppId);
return userState.print(printJobName, adapter, attributes,
resolvedPackageName, resolvedAppId);
} finally {
Binder.restoreCallingIdentity(identity);
}
......@@ -605,6 +606,21 @@ public final class PrintManagerService extends IPrintManager.Stub {
+ "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
}
private String resolveCallingPackageNameEnforcingSecurity(String packageName) {
if (TextUtils.isEmpty(packageName)) {
return null;
}
String[] packages = mContext.getPackageManager().getPackagesForUid(
Binder.getCallingUid());
final int packageCount = packages.length;
for (int i = 0; i < packageCount; i++) {
if (packageName.equals(packages[i])) {
return packageName;
}
}
return null;
}
private void showEnableInstalledPrintServiceNotification(ComponentName component,
String label) {
Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
......
......@@ -26,8 +26,6 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.print.IPrintClient;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintSpooler;
import android.print.IPrintSpoolerCallbacks;
import android.print.IPrintSpoolerClient;
......@@ -130,15 +128,14 @@ final class RemotePrintSpooler {
return null;
}
public final void createPrintJob(PrintJobInfo printJob, IPrintClient client,
IPrintDocumentAdapter documentAdapter) {
public final void createPrintJob(PrintJobInfo printJob) {
throwIfCalledOnMainThread();
synchronized (mLock) {
throwIfDestroyedLocked();
mCanUnbind = false;
}
try {
getRemoteInstanceLazy().createPrintJob(printJob, client, documentAdapter);
getRemoteInstanceLazy().createPrintJob(printJob);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error creating print job.", re);
} catch (TimeoutException te) {
......
......@@ -16,16 +16,20 @@
package com.android.server.print;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
......@@ -33,7 +37,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.print.IPrintClient;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintJobStateChangeListener;
import android.print.IPrinterDiscoveryObserver;
......@@ -44,6 +47,7 @@ import android.print.PrintManager;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.printservice.PrintServiceInfo;
import android.provider.DocumentsContract;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
......@@ -158,9 +162,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
mSpooler.removeObsoletePrintJobs();
}
public PrintJobInfo print(String printJobName, final IPrintClient client,
final IPrintDocumentAdapter documentAdapter, PrintAttributes attributes,
int appId) {
@SuppressWarnings("deprecation")
public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
PrintAttributes attributes, String packageName, int appId) {
// Create print job place holder.
final PrintJobInfo printJob = new PrintJobInfo();
printJob.setId(new PrintJobId());
......@@ -169,9 +173,10 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
printJob.setAttributes(attributes);
printJob.setState(PrintJobInfo.STATE_CREATED);
printJob.setCopies(1);
printJob.setCreationTime(System.currentTimeMillis());
// Track this job so we can forget it when the creator dies.
if (!mPrintJobForAppCache.onPrintJobCreated(client.asBinder(), appId,
if (!mPrintJobForAppCache.onPrintJobCreated(adapter.asBinder(), appId,
printJob)) {
// Not adding a print job means the client is dead - done.
return null;
......@@ -181,12 +186,31 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
mSpooler.createPrintJob(printJob, client, documentAdapter);
mSpooler.createPrintJob(printJob);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
return printJob;
final long identity = Binder.clearCallingIdentity();
try {
Intent intent = new Intent(PrintManager.ACTION_PRINT_DIALOG);
intent.setData(Uri.fromParts("printjob", printJob.getId().flattenToString(), null));
intent.putExtra(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER, adapter.asBinder());
intent.putExtra(PrintManager.EXTRA_PRINT_JOB, printJob);
intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, packageName);
IntentSender intentSender = PendingIntent.getActivity(
mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
Bundle result = new Bundle();
result.putParcelable(PrintManager.EXTRA_PRINT_JOB, printJob);
result.putParcelable(PrintManager.EXTRA_PRINT_DIALOG_INTENT, intentSender);
return result;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
public List<PrintJobInfo> getPrintJobInfos(int appId) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment