Crash in print spooler if printing app killed from recents.

If the printing app with the print UI on top is killed from recents we get
a crash because: 1) the remote print document was not transitioned to a
failed state if the printing app dies (this is an unrecoverable failure);
2) the print preview controller was destroyed asyncronosly during which it
also asynchronously disconnects from the rendering service which however
happens after the system has already cleaned up all connections of the print
UI activity as it is being destoryed.

bug:18109386

Change-Id: If6200b14a8aa90622228bbb659e9c4962226f561
This commit is contained in:
Svet Ganov
2014-10-24 12:53:23 -07:00
parent bd6fabe2ae
commit 4237c92d85
6 changed files with 51 additions and 79 deletions

View File

@@ -110,13 +110,12 @@ public final class PageContentRepository {
mRenderer.close(callback);
}
public void destroy(Runnable callback) {
throwIfNotClosed();
public void destroy() {
mState = STATE_DESTROYED;
if (DEBUG) {
Log.i(LOG_TAG, "STATE_DESTROYED");
}
doDestroy(callback);
mRenderer.destroy();
}
public void startPreload(int firstShownPage, int lastShownPage) {
@@ -163,21 +162,13 @@ public final class PageContentRepository {
try {
if (mState != STATE_DESTROYED) {
mCloseGuard.warnIfOpen();
doDestroy(null);
destroy();
}
} finally {
super.finalize();
}
}
private void doDestroy(Runnable callback) {
mState = STATE_DESTROYED;
if (DEBUG) {
Log.i(LOG_TAG, "STATE_DESTROYED");
}
mRenderer.destroy(callback);
}
private void throwIfNotOpened() {
if (mState != STATE_OPENED) {
throw new IllegalStateException("Not opened");
@@ -428,6 +419,7 @@ public final class PageContentRepository {
private IPdfRenderer mRenderer;
private boolean mBoundToService;
private boolean mDestroyed;
public AsyncRenderer(Context context, OnMalformedPdfFileListener malformedPdfFileListener) {
mContext = context;
@@ -441,7 +433,6 @@ public final class PageContentRepository {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBoundToService = true;
synchronized (mLock) {
mRenderer = IPdfRenderer.Stub.asInterface(service);
mLock.notifyAll();
@@ -465,9 +456,14 @@ public final class PageContentRepository {
new AsyncTask<Void, Void, Integer>() {
@Override
protected void onPreExecute() {
if (mDestroyed) {
cancel(true);
return;
}
Intent intent = new Intent(PdfManipulationService.ACTION_GET_RENDERER);
intent.setClass(mContext, PdfManipulationService.class);
mContext.bindService(intent, AsyncRenderer.this, Context.BIND_AUTO_CREATE);
mBoundToService = true;
}
@Override
@@ -512,6 +508,14 @@ public final class PageContentRepository {
cancelAllRendering();
new AsyncTask<Void, Void, Void>() {
@Override
protected void onPreExecute() {
if (mDestroyed) {
cancel(true);
return;
}
}
@Override
protected Void doInBackground(Void... params) {
synchronized (mLock) {
@@ -534,27 +538,14 @@ public final class PageContentRepository {
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
}
public void destroy(final Runnable callback) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
return null;
}
@Override
public void onPostExecute(Void result) {
if (mBoundToService) {
mBoundToService = false;
mContext.unbindService(AsyncRenderer.this);
}
mPageContentCache.invalidate();
mPageContentCache.clear();
if (callback != null) {
callback.run();
}
}
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
public void destroy() {
if (mBoundToService) {
mBoundToService = false;
mContext.unbindService(AsyncRenderer.this);
}
mPageContentCache.invalidate();
mPageContentCache.clear();
mDestroyed = true;
}
public void startPreload(int firstShownPage, int lastShownPage, RenderSpec renderSpec) {

View File

@@ -78,7 +78,7 @@ public final class PrintSpoolerService extends Service {
private static final boolean DEBUG_PERSISTENCE = false;
private static final boolean PERSISTNECE_MANAGER_ENABLED = true;
private static final boolean PERSISTENCE_MANAGER_ENABLED = true;
private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;
@@ -728,7 +728,7 @@ public final class PrintSpoolerService extends Service {
}
public void writeStateLocked() {
if (!PERSISTNECE_MANAGER_ENABLED) {
if (!PERSISTENCE_MANAGER_ENABLED) {
return;
}
if (mWriteStateScheduled) {
@@ -935,7 +935,7 @@ public final class PrintSpoolerService extends Service {
}
public void readStateLocked() {
if (!PERSISTNECE_MANAGER_ENABLED) {
if (!PERSISTENCE_MANAGER_ENABLED) {
return;
}
FileInputStream in = null;

View File

@@ -137,7 +137,7 @@ public final class RemotePrintDocument {
private final DeathRecipient mDeathRecipient = new DeathRecipient() {
@Override
public void binderDied() {
notifyPrintingAppDied();
onPrintingAppDied();
}
};
@@ -268,7 +268,7 @@ public final class RemotePrintDocument {
mPrintDocumentAdapter.finish();
mState = STATE_FINISHED;
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error calling finish()", re);
Log.e(LOG_TAG, "Error calling finish()");
mState = STATE_FAILED;
}
}
@@ -1108,7 +1108,8 @@ public final class RemotePrintDocument {
}
}
private void notifyPrintingAppDied() {
private void onPrintingAppDied() {
mState = STATE_FAILED;
new Handler(mLooper).post(new Runnable() {
@Override
public void run() {
@@ -1129,7 +1130,7 @@ public final class RemotePrintDocument {
public void onDestroy() {
final RemotePrintDocument document = mWeakDocument.get();
if (document != null) {
document.notifyPrintingAppDied();
document.onPrintingAppDied();
}
}
}

View File

@@ -484,9 +484,13 @@ public final class PageAdapter extends Adapter implements
return selectedPages;
}
public void destroy(Runnable callback) {
throwIfNotClosed();
doDestroy(callback);
public void destroy() {
mPageContentRepository.destroy();
mCloseGuard.close();
mState = STATE_DESTROYED;
if (DEBUG) {
Log.i(LOG_TAG, "STATE_DESTROYED");
}
}
@Override
@@ -494,7 +498,7 @@ public final class PageAdapter extends Adapter implements
try {
if (mState != STATE_DESTROYED) {
mCloseGuard.warnIfOpen();
doDestroy(null);
destroy();
}
} finally {
super.finalize();
@@ -741,15 +745,6 @@ public final class PageAdapter extends Adapter implements
mPageContentRepository.stopPreload();
}
private void doDestroy(Runnable callback) {
mPageContentRepository.destroy(callback);
mCloseGuard.close();
mState = STATE_DESTROYED;
if (DEBUG) {
Log.i(LOG_TAG, "STATE_DESTROYED");
}
}
private void throwIfNotOpened() {
if (mState != STATE_OPENED) {
throw new IllegalStateException("Not opened");

View File

@@ -1616,15 +1616,9 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
mSpoolerProvider.destroy();
mPrintedDocument.finish();
mPrintedDocument.destroy();
mPrintPreviewController.destroy(new Runnable() {
@Override
public void run() {
finish();
}
});
} else {
finish();
mPrintPreviewController.destroy();
}
finish();
}
private final class SpinnerItem<T> {

View File

@@ -192,15 +192,10 @@ class PrintPreviewController implements MutexFileProvider.OnReleaseRequestCallba
});
}
public void destroy(Runnable callback) {
if (mPageAdapter.isOpened()) {
Message operation = mHandler.obtainMessage(MyHandler.MSG_CLOSE);
mHandler.enqueueOperation(operation);
}
Message operation = mHandler.obtainMessage(MyHandler.MSG_DESTROY);
operation.obj = callback;
mHandler.enqueueOperation(operation);
public void destroy() {
mHandler.cancelQueuedOperations();
mRecyclerView.setAdapter(null);
mPageAdapter.destroy();
}
@Override
@@ -226,7 +221,6 @@ class PrintPreviewController implements MutexFileProvider.OnReleaseRequestCallba
private final class MyHandler extends Handler {
public static final int MSG_OPEN = 1;
public static final int MSG_CLOSE = 2;
public static final int MSG_DESTROY = 3;
public static final int MSG_UPDATE = 4;
public static final int MSG_START_PRELOAD = 5;
@@ -246,6 +240,10 @@ class PrintPreviewController implements MutexFileProvider.OnReleaseRequestCallba
super(looper, null, false);
}
public void cancelQueuedOperations() {
mPendingOperations.clear();
}
public void enqueueOperation(Message message) {
mPendingOperations.add(message);
handleNextOperation();
@@ -294,13 +292,6 @@ class PrintPreviewController implements MutexFileProvider.OnReleaseRequestCallba
});
} break;
case MSG_DESTROY: {
Runnable callback = (Runnable) message.obj;
mRecyclerView.setAdapter(null);
mPageAdapter.destroy(callback);
handleNextOperation();
} break;
case MSG_UPDATE: {
SomeArgs args = (SomeArgs) message.obj;
PageRange[] writtenPages = (PageRange[]) args.arg1;