13bf66744d61d18c66d46f2608de0467ad3df0268Mopria/* 23bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Copyright (C) 2016 The Android Open Source Project 33bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Copyright (C) 2016 Mopria Alliance, Inc. 43bf66744d61d18c66d46f2608de0467ad3df0268Mopria * 53bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Licensed under the Apache License, Version 2.0 (the "License"); 63bf66744d61d18c66d46f2608de0467ad3df0268Mopria * you may not use this file except in compliance with the License. 73bf66744d61d18c66d46f2608de0467ad3df0268Mopria * You may obtain a copy of the License at 83bf66744d61d18c66d46f2608de0467ad3df0268Mopria * 93bf66744d61d18c66d46f2608de0467ad3df0268Mopria * http://www.apache.org/licenses/LICENSE-2.0 103bf66744d61d18c66d46f2608de0467ad3df0268Mopria * 113bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Unless required by applicable law or agreed to in writing, software 123bf66744d61d18c66d46f2608de0467ad3df0268Mopria * distributed under the License is distributed on an "AS IS" BASIS, 133bf66744d61d18c66d46f2608de0467ad3df0268Mopria * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 143bf66744d61d18c66d46f2608de0467ad3df0268Mopria * See the License for the specific language governing permissions and 153bf66744d61d18c66d46f2608de0467ad3df0268Mopria * limitations under the License. 163bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 173bf66744d61d18c66d46f2608de0467ad3df0268Mopria 183bf66744d61d18c66d46f2608de0467ad3df0268Mopriapackage com.android.bips.render; 193bf66744d61d18c66d46f2608de0467ad3df0268Mopria 203bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.app.Service; 213bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.content.Intent; 223bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.graphics.Bitmap; 233bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.graphics.Matrix; 243bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.graphics.pdf.PdfRenderer; 253bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.os.IBinder; 263bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.os.ParcelFileDescriptor; 273bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.os.RemoteException; 283bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.util.Log; 293bf66744d61d18c66d46f2608de0467ad3df0268Mopria 303bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport com.android.bips.jni.SizeD; 313bf66744d61d18c66d46f2608de0467ad3df0268Mopria 323bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport java.io.IOException; 333bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport java.io.OutputStream; 343bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport java.nio.ByteBuffer; 353bf66744d61d18c66d46f2608de0467ad3df0268Mopria 363bf66744d61d18c66d46f2608de0467ad3df0268Mopria/** 373bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Implements a PDF rendering service which can be run in an isolated process 383bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 393bf66744d61d18c66d46f2608de0467ad3df0268Mopriapublic class PdfRenderService extends Service { 403bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final String TAG = PdfRenderService.class.getSimpleName(); 413bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final boolean DEBUG = false; 423bf66744d61d18c66d46f2608de0467ad3df0268Mopria 433bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** How large of a chunk of Bitmap data to copy at once to the output stream */ 443bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final int MAX_BYTES_PER_CHUNK = 1024 * 1024 * 5; 453bf66744d61d18c66d46f2608de0467ad3df0268Mopria 463bf66744d61d18c66d46f2608de0467ad3df0268Mopria private PdfRenderer mRenderer; 473bf66744d61d18c66d46f2608de0467ad3df0268Mopria private PdfRenderer.Page mPage; 483bf66744d61d18c66d46f2608de0467ad3df0268Mopria 493bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** Lock held to protect against close() of current page during rendering. */ 503bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final Object mPageOpenLock = new Object(); 513bf66744d61d18c66d46f2608de0467ad3df0268Mopria 523bf66744d61d18c66d46f2608de0467ad3df0268Mopria @Override 533bf66744d61d18c66d46f2608de0467ad3df0268Mopria public IBinder onBind(Intent intent) { 543bf66744d61d18c66d46f2608de0467ad3df0268Mopria return mBinder; 553bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 563bf66744d61d18c66d46f2608de0467ad3df0268Mopria 573bf66744d61d18c66d46f2608de0467ad3df0268Mopria @Override 583bf66744d61d18c66d46f2608de0467ad3df0268Mopria public boolean onUnbind(Intent intent) { 593bf66744d61d18c66d46f2608de0467ad3df0268Mopria closeAll(); 603bf66744d61d18c66d46f2608de0467ad3df0268Mopria return super.onUnbind(intent); 613bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 623bf66744d61d18c66d46f2608de0467ad3df0268Mopria 633bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final IPdfRender.Stub mBinder = new IPdfRender.Stub() { 643bf66744d61d18c66d46f2608de0467ad3df0268Mopria @Override 653bf66744d61d18c66d46f2608de0467ad3df0268Mopria public int openDocument(ParcelFileDescriptor pfd) throws RemoteException { 663bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (!open(pfd)) return 0; 673bf66744d61d18c66d46f2608de0467ad3df0268Mopria return mRenderer.getPageCount(); 683bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 693bf66744d61d18c66d46f2608de0467ad3df0268Mopria 703bf66744d61d18c66d46f2608de0467ad3df0268Mopria @Override 713bf66744d61d18c66d46f2608de0467ad3df0268Mopria public SizeD getPageSize(int page) throws RemoteException { 723bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (!openPage(page)) return null; 733bf66744d61d18c66d46f2608de0467ad3df0268Mopria return new SizeD(mPage.getWidth(), mPage.getHeight()); 743bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 753bf66744d61d18c66d46f2608de0467ad3df0268Mopria 763bf66744d61d18c66d46f2608de0467ad3df0268Mopria @Override 773bf66744d61d18c66d46f2608de0467ad3df0268Mopria public ParcelFileDescriptor renderPageStripe(int page, int y, int width, int height, 783bf66744d61d18c66d46f2608de0467ad3df0268Mopria double zoomFactor) 793bf66744d61d18c66d46f2608de0467ad3df0268Mopria throws RemoteException { 803bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (!openPage(page)) return null; 813bf66744d61d18c66d46f2608de0467ad3df0268Mopria 823bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Create a pipe with input and output sides 833bf66744d61d18c66d46f2608de0467ad3df0268Mopria ParcelFileDescriptor pipes[]; 843bf66744d61d18c66d46f2608de0467ad3df0268Mopria try { 853bf66744d61d18c66d46f2608de0467ad3df0268Mopria pipes = ParcelFileDescriptor.createPipe(); 863bf66744d61d18c66d46f2608de0467ad3df0268Mopria } catch (IOException e) { 873bf66744d61d18c66d46f2608de0467ad3df0268Mopria return null; 883bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 893bf66744d61d18c66d46f2608de0467ad3df0268Mopria 903bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Use a thread to spool out the bitmap data 913bf66744d61d18c66d46f2608de0467ad3df0268Mopria new RenderThread(mPage, y, width, height, zoomFactor, pipes[1]).start(); 923bf66744d61d18c66d46f2608de0467ad3df0268Mopria 933bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Return the corresponding input stream. 943bf66744d61d18c66d46f2608de0467ad3df0268Mopria return pipes[0]; 953bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 963bf66744d61d18c66d46f2608de0467ad3df0268Mopria 973bf66744d61d18c66d46f2608de0467ad3df0268Mopria @Override 983bf66744d61d18c66d46f2608de0467ad3df0268Mopria public void closeDocument() throws RemoteException { 993bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (DEBUG) Log.d(TAG, "closeDocument"); 1003bf66744d61d18c66d46f2608de0467ad3df0268Mopria closeAll(); 1013bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1023bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1033bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** 1043bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Ensure the specified PDF file is open, closing the old file if necessary, and returning 1053bf66744d61d18c66d46f2608de0467ad3df0268Mopria * true if successful. 1063bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 1073bf66744d61d18c66d46f2608de0467ad3df0268Mopria private boolean open(ParcelFileDescriptor pfd) { 1083bf66744d61d18c66d46f2608de0467ad3df0268Mopria closeAll(); 1093bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1103bf66744d61d18c66d46f2608de0467ad3df0268Mopria try { 1113bf66744d61d18c66d46f2608de0467ad3df0268Mopria mRenderer = new PdfRenderer(pfd); 1123bf66744d61d18c66d46f2608de0467ad3df0268Mopria } catch (IOException e) { 1133bf66744d61d18c66d46f2608de0467ad3df0268Mopria Log.w(TAG, "Could not open file descriptor for rendering", e); 1143bf66744d61d18c66d46f2608de0467ad3df0268Mopria return false; 1153bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1163bf66744d61d18c66d46f2608de0467ad3df0268Mopria return true; 1173bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1183bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1193bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** 1203bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Ensure the specified PDF file and page are open, closing the old file if necessary, and 1213bf66744d61d18c66d46f2608de0467ad3df0268Mopria * returning true if successful. 1223bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 1233bf66744d61d18c66d46f2608de0467ad3df0268Mopria private boolean openPage(int page) { 1243bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mRenderer == null) return false; 1253bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1263bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Close old page if this is a new page 1273bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mPage != null && mPage.getIndex() != page) { 1283bf66744d61d18c66d46f2608de0467ad3df0268Mopria closePage(); 1293bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1303bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1313bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Open new page if necessary 1323bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mPage == null) { 1333bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPage = mRenderer.openPage(page); 1343bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1353bf66744d61d18c66d46f2608de0467ad3df0268Mopria return true; 1363bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1373bf66744d61d18c66d46f2608de0467ad3df0268Mopria }; 1383bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1393bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** Close the current page if one is open */ 1403bf66744d61d18c66d46f2608de0467ad3df0268Mopria private void closePage() { 1413bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mPage != null) { 1423bf66744d61d18c66d46f2608de0467ad3df0268Mopria synchronized (mPageOpenLock) { 1433bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPage.close(); 1443bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1453bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPage = null; 1463bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1473bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1483bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1493bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** 1503bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Close the current page and file if open 1513bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 1523bf66744d61d18c66d46f2608de0467ad3df0268Mopria private void closeAll() { 1533bf66744d61d18c66d46f2608de0467ad3df0268Mopria closePage(); 1543bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1553bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mRenderer != null) { 1563bf66744d61d18c66d46f2608de0467ad3df0268Mopria mRenderer.close(); 1573bf66744d61d18c66d46f2608de0467ad3df0268Mopria mRenderer = null; 1583bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1593bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1603bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1613bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** 1623bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Renders page data to RGB bytes and writes them to an output stream 1633bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 1643bf66744d61d18c66d46f2608de0467ad3df0268Mopria private class RenderThread extends Thread { 1653bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final PdfRenderer.Page mPage; 1663bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final int mWidth; 1673bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final int mYOffset; 1683bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final int mHeight; 1693bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final double mZoomFactor; 1703bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final int mRowsPerStripe; 1713bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final ParcelFileDescriptor mOutput; 1723bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final ByteBuffer mBuffer; 1733bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1743bf66744d61d18c66d46f2608de0467ad3df0268Mopria RenderThread(PdfRenderer.Page page, int y, int width, int height, double zoom, 1753bf66744d61d18c66d46f2608de0467ad3df0268Mopria ParcelFileDescriptor output) { 1763bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPage = page; 1773bf66744d61d18c66d46f2608de0467ad3df0268Mopria mWidth = width; 1783bf66744d61d18c66d46f2608de0467ad3df0268Mopria mYOffset = y; 1793bf66744d61d18c66d46f2608de0467ad3df0268Mopria mHeight = height; 1803bf66744d61d18c66d46f2608de0467ad3df0268Mopria mZoomFactor = zoom; 1813bf66744d61d18c66d46f2608de0467ad3df0268Mopria mOutput = output; 1823bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1833bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Buffer will temporarily hold RGBA data from Bitmap 1843bf66744d61d18c66d46f2608de0467ad3df0268Mopria mRowsPerStripe = MAX_BYTES_PER_CHUNK / mWidth / 4; 1853bf66744d61d18c66d46f2608de0467ad3df0268Mopria mBuffer = ByteBuffer.allocate(mWidth * mRowsPerStripe * 4); 1863bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1873bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1883bf66744d61d18c66d46f2608de0467ad3df0268Mopria @Override 1893bf66744d61d18c66d46f2608de0467ad3df0268Mopria public void run() { 1903bf66744d61d18c66d46f2608de0467ad3df0268Mopria Bitmap bitmap = null; 1913bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1923bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Make sure nobody closes page while we're using it 1933bf66744d61d18c66d46f2608de0467ad3df0268Mopria synchronized(mPageOpenLock) { 1943bf66744d61d18c66d46f2608de0467ad3df0268Mopria try (OutputStream outputStream = new ParcelFileDescriptor.AutoCloseOutputStream( 1953bf66744d61d18c66d46f2608de0467ad3df0268Mopria mOutput)) { 1963bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mPage == null) { 1973bf66744d61d18c66d46f2608de0467ad3df0268Mopria // If page was closed before we synchronized, this closes the outputStream 1983bf66744d61d18c66d46f2608de0467ad3df0268Mopria Log.e(TAG, "Page lost"); 1993bf66744d61d18c66d46f2608de0467ad3df0268Mopria return; 2003bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2013bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Allocate and clear bitmap to white with no transparency 2023bf66744d61d18c66d46f2608de0467ad3df0268Mopria bitmap = Bitmap.createBitmap(mWidth, mRowsPerStripe, Bitmap.Config.ARGB_8888); 2033bf66744d61d18c66d46f2608de0467ad3df0268Mopria 2043bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Render each stripe to output 2053bf66744d61d18c66d46f2608de0467ad3df0268Mopria for (int startRow = mYOffset; startRow < mYOffset + mHeight; startRow += 2063bf66744d61d18c66d46f2608de0467ad3df0268Mopria mRowsPerStripe) { 2073bf66744d61d18c66d46f2608de0467ad3df0268Mopria int stripeRows = Math.min(mRowsPerStripe, (mYOffset + mHeight) - startRow); 2083bf66744d61d18c66d46f2608de0467ad3df0268Mopria renderToBitmap(startRow, bitmap); 2093bf66744d61d18c66d46f2608de0467ad3df0268Mopria writeRgb(bitmap, stripeRows, outputStream); 2103bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2113bf66744d61d18c66d46f2608de0467ad3df0268Mopria } catch (IOException e) { 2123bf66744d61d18c66d46f2608de0467ad3df0268Mopria Log.e(TAG, "Failed to write", e); 2133bf66744d61d18c66d46f2608de0467ad3df0268Mopria } finally { 2143bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (bitmap != null) bitmap.recycle(); 2153bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2163bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2173bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2183bf66744d61d18c66d46f2608de0467ad3df0268Mopria 2193bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** From the specified starting row, render from the current page into the target bitmap */ 2203bf66744d61d18c66d46f2608de0467ad3df0268Mopria private void renderToBitmap(int startRow, Bitmap bitmap) { 2213bf66744d61d18c66d46f2608de0467ad3df0268Mopria Matrix matrix = new Matrix(); 2223bf66744d61d18c66d46f2608de0467ad3df0268Mopria // The scaling matrix increases DPI (default is 72dpi) to page output 2233bf66744d61d18c66d46f2608de0467ad3df0268Mopria matrix.setScale((float) mZoomFactor, (float) mZoomFactor); 2243bf66744d61d18c66d46f2608de0467ad3df0268Mopria // The translate specifies adjusts which part of the page we are rendering 2253bf66744d61d18c66d46f2608de0467ad3df0268Mopria matrix.postTranslate(0, 0 - startRow); 2263bf66744d61d18c66d46f2608de0467ad3df0268Mopria bitmap.eraseColor(0xFFFFFFFF); 2273bf66744d61d18c66d46f2608de0467ad3df0268Mopria 2283bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPage.render(bitmap, null, matrix, PdfRenderer.Page.RENDER_MODE_FOR_PRINT); 2293bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2303bf66744d61d18c66d46f2608de0467ad3df0268Mopria 2313bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** Copy rows of RGB bytes from the bitmap to the output stream */ 2323bf66744d61d18c66d46f2608de0467ad3df0268Mopria private void writeRgb(Bitmap bitmap, int rows, OutputStream out) 2333bf66744d61d18c66d46f2608de0467ad3df0268Mopria throws IOException { 2343bf66744d61d18c66d46f2608de0467ad3df0268Mopria mBuffer.clear(); 2353bf66744d61d18c66d46f2608de0467ad3df0268Mopria bitmap.copyPixelsToBuffer(mBuffer); 2363bf66744d61d18c66d46f2608de0467ad3df0268Mopria int alphaPixelSize = mWidth * rows * 4; 2373bf66744d61d18c66d46f2608de0467ad3df0268Mopria 2383bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Chop out the alpha byte 2393bf66744d61d18c66d46f2608de0467ad3df0268Mopria byte array[] = mBuffer.array(); 2403bf66744d61d18c66d46f2608de0467ad3df0268Mopria int from, to; 2413bf66744d61d18c66d46f2608de0467ad3df0268Mopria for (from = 0, to = 0; from < alphaPixelSize; from += 4, to += 3) { 2423bf66744d61d18c66d46f2608de0467ad3df0268Mopria array[to] = array[from]; 2433bf66744d61d18c66d46f2608de0467ad3df0268Mopria array[to + 1] = array[from + 1]; 2443bf66744d61d18c66d46f2608de0467ad3df0268Mopria array[to + 2] = array[from + 2]; 2453bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2463bf66744d61d18c66d46f2608de0467ad3df0268Mopria 2473bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Write it 2483bf66744d61d18c66d46f2608de0467ad3df0268Mopria out.write(mBuffer.array(), 0, to); 2493bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2503bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2513bf66744d61d18c66d46f2608de0467ad3df0268Mopria}