Merge "Crash apps that print malformed or password protected PDFs." into lmp-mr1-dev

This commit is contained in:
Svet Ganov
2014-11-01 00:22:07 +00:00
committed by Android (Google) Code Review
12 changed files with 165 additions and 39 deletions

View File

@@ -37,4 +37,5 @@ oneway interface IPrintDocumentAdapter {
void write(in PageRange[] pages, in ParcelFileDescriptor fd, void write(in PageRange[] pages, in ParcelFileDescriptor fd,
IWriteResultCallback callback, int sequence); IWriteResultCallback callback, int sequence);
void finish(); void finish();
void kill(String reason);
} }

View File

@@ -633,6 +633,17 @@ public final class PrintManager {
} }
} }
@Override
public void kill(String reason) {
synchronized (mLock) {
// If destroyed the handler is null.
if (!isDestroyedLocked()) {
mHandler.obtainMessage(MyHandler.MSG_ON_KILL,
reason).sendToTarget();
}
}
}
@Override @Override
public void onActivityPaused(Activity activity) { public void onActivityPaused(Activity activity) {
/* do nothing */ /* do nothing */
@@ -719,6 +730,7 @@ public final class PrintManager {
public static final int MSG_ON_LAYOUT = 2; public static final int MSG_ON_LAYOUT = 2;
public static final int MSG_ON_WRITE = 3; public static final int MSG_ON_WRITE = 3;
public static final int MSG_ON_FINISH = 4; public static final int MSG_ON_FINISH = 4;
public static final int MSG_ON_KILL = 5;
public MyHandler(Looper looper) { public MyHandler(Looper looper) {
super(looper, null, true); super(looper, null, true);
@@ -794,6 +806,15 @@ public final class PrintManager {
} }
} break; } break;
case MSG_ON_KILL: {
if (DEBUG) {
Log.i(LOG_TAG, "onKill()");
}
String reason = (String) message.obj;
throw new RuntimeException(reason);
}
default: { default: {
throw new IllegalArgumentException("Unknown message: " throw new IllegalArgumentException("Unknown message: "
+ message.what); + message.what);

View File

@@ -89,8 +89,17 @@ static jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size) {
if (!document) { if (!document) {
const long error = FPDF_GetLastError(); const long error = FPDF_GetLastError();
jniThrowException(env, "java/io/IOException", switch (error) {
"cannot create document. Error:" + error); case FPDF_ERR_PASSWORD:
case FPDF_ERR_SECURITY: {
jniThrowException(env, "java/lang/SecurityException",
"cannot create document. Error:" + error);
} break;
default: {
jniThrowException(env, "java/io/IOException",
"cannot create document. Error:" + error);
} break;
}
destroyLibraryIfNeeded(); destroyLibraryIfNeeded();
return -1; return -1;
} }

View File

@@ -82,8 +82,17 @@ static jlong nativeCreate(JNIEnv* env, jclass thiz, jint fd, jlong size) {
if (!document) { if (!document) {
const long error = FPDF_GetLastError(); const long error = FPDF_GetLastError();
jniThrowException(env, "java/io/IOException", switch (error) {
"cannot create document. Error:" + error); case FPDF_ERR_PASSWORD:
case FPDF_ERR_SECURITY: {
jniThrowException(env, "java/lang/SecurityException",
"cannot create document. Error:" + error);
} break;
default: {
jniThrowException(env, "java/io/IOException",
"cannot create document. Error:" + error);
} break;
}
destroyLibraryIfNeeded(); destroyLibraryIfNeeded();
return -1; return -1;
} }

View File

@@ -59,6 +59,10 @@ public final class PdfEditor {
* *
* @param input Seekable file descriptor to read from. * @param input Seekable file descriptor to read from.
* *
* @throws java.io.IOException If an error occurs while reading the file.
* @throws java.lang.SecurityException If the file requires a password or
* the security scheme is not supported.
*
* @see #close() * @see #close()
*/ */
public PdfEditor(@NonNull ParcelFileDescriptor input) throws IOException { public PdfEditor(@NonNull ParcelFileDescriptor input) throws IOException {

View File

@@ -131,6 +131,10 @@ public final class PdfRenderer implements AutoCloseable {
* </p> * </p>
* *
* @param input Seekable file descriptor to read from. * @param input Seekable file descriptor to read from.
*
* @throws java.io.IOException If an error occurs while reading the file.
* @throws java.lang.SecurityException If the file requires a password or
* the security scheme is not supported.
*/ */
public PdfRenderer(@NonNull ParcelFileDescriptor input) throws IOException { public PdfRenderer(@NonNull ParcelFileDescriptor input) throws IOException {
if (input == null) { if (input == null) {

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2014 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.model;
/**
* Callbacks interface for opening a file.
*/
public interface OpenDocumentCallback {
public static final int ERROR_MALFORMED_PDF_FILE = -1;
public static final int ERROR_SECURE_PDF_FILE = -2;
/**
* Called after the file is opened.
*/
public void onSuccess();
/**
* Called after opening the file failed.
*
* @param error The error.
*/
public void onFailure(int error);
}

View File

@@ -78,13 +78,8 @@ public final class PageContentRepository {
public void onPageContentAvailable(BitmapDrawable content); public void onPageContentAvailable(BitmapDrawable content);
} }
public interface OnMalformedPdfFileListener { public PageContentRepository(Context context) {
public void onMalformedPdfFile(); mRenderer = new AsyncRenderer(context);
}
public PageContentRepository(Context context,
OnMalformedPdfFileListener malformedPdfFileListener) {
mRenderer = new AsyncRenderer(context, malformedPdfFileListener);
mState = STATE_CLOSED; mState = STATE_CLOSED;
if (DEBUG) { if (DEBUG) {
Log.i(LOG_TAG, "STATE_CLOSED"); Log.i(LOG_TAG, "STATE_CLOSED");
@@ -92,7 +87,7 @@ public final class PageContentRepository {
mCloseGuard.open("destroy"); mCloseGuard.open("destroy");
} }
public void open(ParcelFileDescriptor source, final Runnable callback) { public void open(ParcelFileDescriptor source, final OpenDocumentCallback callback) {
throwIfNotClosed(); throwIfNotClosed();
mState = STATE_OPENED; mState = STATE_OPENED;
if (DEBUG) { if (DEBUG) {
@@ -412,8 +407,6 @@ public final class PageContentRepository {
private final ArrayMap<Integer, RenderPageTask> mPageToRenderTaskMap = new ArrayMap<>(); private final ArrayMap<Integer, RenderPageTask> mPageToRenderTaskMap = new ArrayMap<>();
private final OnMalformedPdfFileListener mOnMalformedPdfFileListener;
private int mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN; private int mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
@GuardedBy("mLock") @GuardedBy("mLock")
@@ -422,9 +415,8 @@ public final class PageContentRepository {
private boolean mBoundToService; private boolean mBoundToService;
private boolean mDestroyed; private boolean mDestroyed;
public AsyncRenderer(Context context, OnMalformedPdfFileListener malformedPdfFileListener) { public AsyncRenderer(Context context) {
mContext = context; mContext = context;
mOnMalformedPdfFileListener = malformedPdfFileListener;
ActivityManager activityManager = (ActivityManager) ActivityManager activityManager = (ActivityManager)
mContext.getSystemService(Context.ACTIVITY_SERVICE); mContext.getSystemService(Context.ACTIVITY_SERVICE);
@@ -447,7 +439,7 @@ public final class PageContentRepository {
} }
} }
public void open(final ParcelFileDescriptor source, final Runnable callback) { public void open(final ParcelFileDescriptor source, final OpenDocumentCallback callback) {
// Opening a new document invalidates the cache as it has pages // Opening a new document invalidates the cache as it has pages
// from the last document. We keep the cache even when the document // from the last document. We keep the cache even when the document
// is closed to show pages while the other side is writing the new // is closed to show pages while the other side is writing the new
@@ -483,7 +475,7 @@ public final class PageContentRepository {
return mRenderer.openDocument(source); return mRenderer.openDocument(source);
} catch (RemoteException re) { } catch (RemoteException re) {
Log.e(LOG_TAG, "Cannot open PDF document"); Log.e(LOG_TAG, "Cannot open PDF document");
return PdfManipulationService.MALFORMED_PDF_FILE_ERROR; return PdfManipulationService.ERROR_MALFORMED_PDF_FILE;
} finally { } finally {
// Close the fd as we passed it to another process // Close the fd as we passed it to another process
// which took ownership. // which took ownership.
@@ -494,14 +486,25 @@ public final class PageContentRepository {
@Override @Override
public void onPostExecute(Integer pageCount) { public void onPostExecute(Integer pageCount) {
if (pageCount == PdfManipulationService.MALFORMED_PDF_FILE_ERROR) { switch (pageCount) {
mOnMalformedPdfFileListener.onMalformedPdfFile(); case PdfManipulationService.ERROR_MALFORMED_PDF_FILE: {
mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN; mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
} else { if (callback != null) {
mPageCount = pageCount; callback.onFailure(OpenDocumentCallback.ERROR_MALFORMED_PDF_FILE);
} }
if (callback != null) { } break;
callback.run(); case PdfManipulationService.ERROR_SECURE_PDF_FILE: {
mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
if (callback != null) {
callback.onFailure(OpenDocumentCallback.ERROR_SECURE_PDF_FILE);
}
} break;
default: {
mPageCount = pageCount;
if (callback != null) {
callback.onSuccess();
}
} break;
} }
} }
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);

View File

@@ -304,6 +304,18 @@ public final class RemotePrintDocument {
disconnectFromRemoteDocument(); disconnectFromRemoteDocument();
} }
public void kill(String reason) {
if (DEBUG) {
Log.i(LOG_TAG, "[CALLED] kill()");
}
try {
mPrintDocumentAdapter.kill(reason);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error calling kill()", re);
}
}
public boolean isUpdating() { public boolean isUpdating() {
return mState == STATE_UPDATING || mState == STATE_CANCELING; return mState == STATE_UPDATING || mState == STATE_CANCELING;
} }

View File

@@ -47,7 +47,9 @@ public final class PdfManipulationService extends Service {
public static final String ACTION_GET_EDITOR = public static final String ACTION_GET_EDITOR =
"com.android.printspooler.renderer.ACTION_GET_EDITOR"; "com.android.printspooler.renderer.ACTION_GET_EDITOR";
public static final int MALFORMED_PDF_FILE_ERROR = -2; public static final int ERROR_MALFORMED_PDF_FILE = -2;
public static final int ERROR_SECURE_PDF_FILE = -3;
private static final String LOG_TAG = "PdfManipulationService"; private static final String LOG_TAG = "PdfManipulationService";
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
@@ -90,7 +92,11 @@ public final class PdfManipulationService extends Service {
} catch (IOException | IllegalStateException e) { } catch (IOException | IllegalStateException e) {
IoUtils.closeQuietly(source); IoUtils.closeQuietly(source);
Log.e(LOG_TAG, "Cannot open file", e); Log.e(LOG_TAG, "Cannot open file", e);
return MALFORMED_PDF_FILE_ERROR; return ERROR_MALFORMED_PDF_FILE;
} catch (SecurityException e) {
IoUtils.closeQuietly(source);
Log.e(LOG_TAG, "Cannot open file", e);
return ERROR_SECURE_PDF_FILE;
} }
} }
} }

View File

@@ -37,6 +37,7 @@ import android.view.ViewGroup.LayoutParams;
import android.view.View.MeasureSpec; import android.view.View.MeasureSpec;
import android.widget.TextView; import android.widget.TextView;
import com.android.printspooler.R; import com.android.printspooler.R;
import com.android.printspooler.model.OpenDocumentCallback;
import com.android.printspooler.model.PageContentRepository; import com.android.printspooler.model.PageContentRepository;
import com.android.printspooler.model.PageContentRepository.PageContentProvider; import com.android.printspooler.model.PageContentRepository.PageContentProvider;
import com.android.printspooler.util.PageRangeUtils; import com.android.printspooler.util.PageRangeUtils;
@@ -51,8 +52,7 @@ import java.util.List;
/** /**
* This class represents the adapter for the pages in the print preview list. * This class represents the adapter for the pages in the print preview list.
*/ */
public final class PageAdapter extends Adapter implements public final class PageAdapter extends Adapter {
PageContentRepository.OnMalformedPdfFileListener {
private static final String LOG_TAG = "PageAdapter"; private static final String LOG_TAG = "PageAdapter";
private static final int MAX_PREVIEW_PAGES_BATCH = 50; private static final int MAX_PREVIEW_PAGES_BATCH = 50;
@@ -113,6 +113,7 @@ public final class PageAdapter extends Adapter implements
public interface ContentCallbacks { public interface ContentCallbacks {
public void onRequestContentUpdate(); public void onRequestContentUpdate();
public void onMalformedPdfFile(); public void onMalformedPdfFile();
public void onSecurePdfFile();
} }
public interface PreviewArea { public interface PreviewArea {
@@ -127,7 +128,7 @@ public final class PageAdapter extends Adapter implements
mCallbacks = callbacks; mCallbacks = callbacks;
mLayoutInflater = (LayoutInflater) context.getSystemService( mLayoutInflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE); Context.LAYOUT_INFLATER_SERVICE);
mPageContentRepository = new PageContentRepository(context, this); mPageContentRepository = new PageContentRepository(context);
mPreviewPageMargin = mContext.getResources().getDimensionPixelSize( mPreviewPageMargin = mContext.getResources().getDimensionPixelSize(
R.dimen.preview_page_margin); R.dimen.preview_page_margin);
@@ -156,11 +157,6 @@ public final class PageAdapter extends Adapter implements
} }
} }
@Override
public void onMalformedPdfFile() {
mCallbacks.onMalformedPdfFile();
}
public void onOrientationChanged() { public void onOrientationChanged() {
mColumnCount = mContext.getResources().getInteger( mColumnCount = mContext.getResources().getInteger(
R.integer.preview_page_per_row_count); R.integer.preview_page_per_row_count);
@@ -181,12 +177,25 @@ public final class PageAdapter extends Adapter implements
if (DEBUG) { if (DEBUG) {
Log.i(LOG_TAG, "STATE_OPENED"); Log.i(LOG_TAG, "STATE_OPENED");
} }
mPageContentRepository.open(source, new Runnable() { mPageContentRepository.open(source, new OpenDocumentCallback() {
@Override @Override
public void run() { public void onSuccess() {
notifyDataSetChanged(); notifyDataSetChanged();
callback.run(); callback.run();
} }
@Override
public void onFailure(int error) {
switch (error) {
case OpenDocumentCallback.ERROR_MALFORMED_PDF_FILE: {
mCallbacks.onMalformedPdfFile();
} break;
case OpenDocumentCallback.ERROR_SECURE_PDF_FILE: {
mCallbacks.onSecurePdfFile();
} break;
}
}
}); });
} }

View File

@@ -378,7 +378,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
return true; return true;
} }
if (mState == STATE_PRINT_CANCELED ||mState == STATE_PRINT_CONFIRMED if (mState == STATE_PRINT_CANCELED || mState == STATE_PRINT_CONFIRMED
|| mState == STATE_PRINT_COMPLETED) { || mState == STATE_PRINT_COMPLETED) {
return true; return true;
} }
@@ -405,12 +405,23 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
@Override @Override
public void onMalformedPdfFile() { public void onMalformedPdfFile() {
onPrintDocumentError("Cannot print a malformed PDF file");
}
@Override
public void onSecurePdfFile() {
onPrintDocumentError("Cannot print a password protected PDF file");
}
private void onPrintDocumentError(String message) {
mProgressMessageController.cancel(); mProgressMessageController.cancel();
ensureErrorUiShown(null, PrintErrorFragment.ACTION_RETRY); ensureErrorUiShown(null, PrintErrorFragment.ACTION_RETRY);
setState(STATE_UPDATE_FAILED); setState(STATE_UPDATE_FAILED);
updateOptionsUi(); updateOptionsUi();
mPrintedDocument.kill(message);
} }
@Override @Override