| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.photo_picker; | 5 package org.chromium.chrome.browser.photo_picker; |
| 6 | 6 |
| 7 import android.app.Service; | 7 import android.app.Service; |
| 8 import android.content.Intent; | 8 import android.content.Intent; |
| 9 import android.graphics.Bitmap; | 9 import android.graphics.Bitmap; |
| 10 import android.os.Bundle; | 10 import android.os.Bundle; |
| 11 import android.os.Handler; | |
| 12 import android.os.IBinder; | 11 import android.os.IBinder; |
| 13 import android.os.Message; | |
| 14 import android.os.Messenger; | |
| 15 import android.os.ParcelFileDescriptor; | 12 import android.os.ParcelFileDescriptor; |
| 16 import android.os.RemoteException; | 13 import android.os.RemoteException; |
| 17 import android.os.SystemClock; | 14 import android.os.SystemClock; |
| 18 | 15 |
| 19 import org.chromium.base.Log; | 16 import org.chromium.base.Log; |
| 20 | 17 |
| 21 import java.io.FileDescriptor; | 18 import java.io.FileDescriptor; |
| 22 import java.io.IOException; | 19 import java.io.IOException; |
| 23 | 20 |
| 24 /** | 21 /** |
| 25 * A service to accept requests to take image file contents and decode them. | 22 * A service to accept requests to take image file contents and decode them. |
| 26 */ | 23 */ |
| 27 public class DecoderService extends Service { | 24 public class DecoderService extends Service { |
| 28 // Message ids for communicating with the client. | |
| 29 | |
| 30 // A message sent by the client to decode an image. | |
| 31 static final int MSG_DECODE_IMAGE = 1; | |
| 32 // A message sent by the server to notify the client of the results of the d
ecoding. | |
| 33 static final int MSG_IMAGE_DECODED_REPLY = 2; | |
| 34 | |
| 35 // The keys for the bundle when passing data to and from this service. | 25 // The keys for the bundle when passing data to and from this service. |
| 36 static final String KEY_FILE_DESCRIPTOR = "file_descriptor"; | 26 static final String KEY_FILE_DESCRIPTOR = "file_descriptor"; |
| 37 static final String KEY_FILE_PATH = "file_path"; | 27 static final String KEY_FILE_PATH = "file_path"; |
| 38 static final String KEY_IMAGE_BITMAP = "image_bitmap"; | 28 static final String KEY_IMAGE_BITMAP = "image_bitmap"; |
| 39 static final String KEY_IMAGE_BYTE_COUNT = "image_byte_count"; | |
| 40 static final String KEY_IMAGE_DESCRIPTOR = "image_descriptor"; | |
| 41 static final String KEY_SIZE = "size"; | 29 static final String KEY_SIZE = "size"; |
| 42 static final String KEY_SUCCESS = "success"; | 30 static final String KEY_SUCCESS = "success"; |
| 43 static final String KEY_DECODE_TIME = "decode_time"; | 31 static final String KEY_DECODE_TIME = "decode_time"; |
| 44 | 32 |
| 45 // A tag for logging error messages. | 33 // A tag for logging error messages. |
| 46 private static final String TAG = "ImageDecoder"; | 34 private static final String TAG = "ImageDecoder"; |
| 47 | 35 |
| 48 /** | 36 @Override |
| 49 * Handler for incoming messages from clients. | 37 public void onCreate() { |
| 50 */ | 38 super.onCreate(); |
| 51 static class IncomingHandler extends Handler { | 39 } |
| 52 @Override | |
| 53 public void handleMessage(Message msg) { | |
| 54 switch (msg.what) { | |
| 55 case MSG_DECODE_IMAGE: | |
| 56 Bundle bundle = null; | |
| 57 Messenger client = null; | |
| 58 String filePath = ""; | |
| 59 int size = 0; | |
| 60 try { | |
| 61 Bundle payload = msg.getData(); | |
| 62 client = msg.replyTo; | |
| 63 | 40 |
| 64 filePath = payload.getString(KEY_FILE_PATH); | 41 @Override |
| 65 ParcelFileDescriptor pfd = payload.getParcelable(KEY_FIL
E_DESCRIPTOR); | 42 public IBinder onBind(Intent intent) { |
| 66 size = payload.getInt(KEY_SIZE); | 43 return mBinder; |
| 44 } |
| 67 | 45 |
| 68 // Setup a minimum viable response to parent process. Wi
ll be fleshed out | 46 private final IDecoderService.Stub mBinder = new IDecoderService.Stub() { |
| 69 // further below. | 47 public void decodeImage(Bundle payload, IDecoderServiceCallback callback
) { |
| 70 bundle = new Bundle(); | 48 Bundle bundle = null; |
| 71 bundle.putString(KEY_FILE_PATH, filePath); | 49 String filePath = ""; |
| 72 bundle.putBoolean(KEY_SUCCESS, false); | 50 int size = 0; |
| 51 try { |
| 52 filePath = payload.getString(KEY_FILE_PATH); |
| 53 ParcelFileDescriptor pfd = payload.getParcelable(KEY_FILE_DESCRI
PTOR); |
| 54 size = payload.getInt(KEY_SIZE); |
| 73 | 55 |
| 74 FileDescriptor fd = pfd.getFileDescriptor(); | 56 // Setup a minimum viable response to parent process. Will be fl
eshed out |
| 57 // further below. |
| 58 bundle = new Bundle(); |
| 59 bundle.putString(KEY_FILE_PATH, filePath); |
| 60 bundle.putBoolean(KEY_SUCCESS, false); |
| 75 | 61 |
| 76 long begin = SystemClock.elapsedRealtime(); | 62 FileDescriptor fd = pfd.getFileDescriptor(); |
| 77 Bitmap bitmap = BitmapUtils.decodeBitmapFromFileDescript
or(fd, size); | |
| 78 long decodeTime = SystemClock.elapsedRealtime() - begin; | |
| 79 | 63 |
| 80 try { | 64 long begin = SystemClock.elapsedRealtime(); |
| 81 pfd.close(); | 65 Bitmap bitmap = BitmapUtils.decodeBitmapFromFileDescriptor(fd, s
ize); |
| 82 } catch (IOException e) { | 66 long decodeTime = SystemClock.elapsedRealtime() - begin; |
| 83 Log.e(TAG, "Closing failed " + filePath + " (size: "
+ size + ") " + e); | |
| 84 } | |
| 85 | 67 |
| 86 if (bitmap == null) { | 68 try { |
| 87 Log.e(TAG, "Decode failed " + filePath + " (size: "
+ size + ")"); | 69 pfd.close(); |
| 88 sendReply(client, bundle); // Sends SUCCESS == false
; | 70 } catch (IOException e) { |
| 89 return; | 71 Log.e(TAG, "Closing failed " + filePath + " (size: " + size
+ ") " + e); |
| 90 } | 72 } |
| 91 | 73 |
| 92 // The most widely supported, easiest, and reasonably ef
ficient method is to | 74 if (bitmap == null) { |
| 93 // decode to an immutable bitmap and just return the bit
map over binder. It | 75 Log.e(TAG, "Decode failed " + filePath + " (size: " + size +
")"); |
| 94 // will internally memcpy itself to ashmem and then just
send over the file | 76 sendReply(callback, bundle); // Sends SUCCESS == false; |
| 95 // descriptor. In the receiving process it will just lea
ve the bitmap on | 77 return; |
| 96 // ashmem since it's immutable and carry on. | 78 } |
| 97 bundle.putParcelable(KEY_IMAGE_BITMAP, bitmap); | |
| 98 bundle.putBoolean(KEY_SUCCESS, true); | |
| 99 bundle.putLong(KEY_DECODE_TIME, decodeTime); | |
| 100 sendReply(client, bundle); | |
| 101 bitmap.recycle(); | |
| 102 } catch (Exception e) { | |
| 103 // This service has no UI and maintains no state so if i
t crashes on | |
| 104 // decoding a photo, it is better UX to eat the exceptio
n instead of showing | |
| 105 // a crash dialog and discarding other requests that hav
e already been sent. | |
| 106 Log.e(TAG, | |
| 107 "Unexpected error during decoding " + filePath +
" (size: " + size | |
| 108 + ") " + e); | |
| 109 | 79 |
| 110 if (bundle != null && client != null) sendReply(client,
bundle); | 80 // The most widely supported, easiest, and reasonably efficient
method is to |
| 111 } | 81 // decode to an immutable bitmap and just return the bitmap over
binder. It |
| 112 break; | 82 // will internally memcpy itself to ashmem and then just send ov
er the file |
| 113 default: | 83 // descriptor. In the receiving process it will just leave the b
itmap on |
| 114 super.handleMessage(msg); | 84 // ashmem since it's immutable and carry on. |
| 85 bundle.putParcelable(KEY_IMAGE_BITMAP, bitmap); |
| 86 bundle.putBoolean(KEY_SUCCESS, true); |
| 87 bundle.putLong(KEY_DECODE_TIME, decodeTime); |
| 88 sendReply(callback, bundle); |
| 89 bitmap.recycle(); |
| 90 } catch (Exception e) { |
| 91 // This service has no UI and maintains no state so if it crashe
s on |
| 92 // decoding a photo, it is better UX to eat the exception instea
d of showing |
| 93 // a crash dialog and discarding other requests that have alread
y been sent. |
| 94 Log.e(TAG, |
| 95 "Unexpected error during decoding " + filePath + " (size
: " + size + ") " |
| 96 + e); |
| 97 |
| 98 if (bundle != null) sendReply(callback, bundle); |
| 115 } | 99 } |
| 116 } | 100 } |
| 117 | 101 |
| 118 private void sendReply(Messenger client, Bundle bundle) { | 102 private void sendReply(IDecoderServiceCallback callback, Bundle bundle)
{ |
| 119 Message reply = Message.obtain(null, MSG_IMAGE_DECODED_REPLY); | |
| 120 reply.setData(bundle); | |
| 121 try { | 103 try { |
| 122 client.send(reply); | 104 callback.onDecodeImageDone(bundle); |
| 123 } catch (RemoteException remoteException) { | 105 } catch (RemoteException remoteException) { |
| 124 Log.e(TAG, "Remote error while replying: " + remoteException); | 106 Log.e(TAG, "Remote error while replying: " + remoteException); |
| 125 } | 107 } |
| 126 } | 108 } |
| 127 } | 109 }; |
| 128 | |
| 129 /** | |
| 130 * The target we publish for clients to send messages to IncomingHandler. | |
| 131 */ | |
| 132 final Messenger mMessenger = new Messenger(new IncomingHandler()); | |
| 133 | |
| 134 /** | |
| 135 * When binding to the service, we return an interface to our messenger | |
| 136 * for sending messages to the service. | |
| 137 */ | |
| 138 @Override | |
| 139 public IBinder onBind(Intent intent) { | |
| 140 return mMessenger.getBinder(); | |
| 141 } | |
| 142 } | 110 } |
| OLD | NEW |