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.content.ComponentName; | 7 import android.content.ComponentName; |
8 import android.content.Context; | 8 import android.content.Context; |
9 import android.content.Intent; | 9 import android.content.Intent; |
10 import android.content.ServiceConnection; | 10 import android.content.ServiceConnection; |
11 import android.graphics.Bitmap; | 11 import android.graphics.Bitmap; |
12 import android.os.Bundle; | 12 import android.os.Bundle; |
13 import android.os.Handler; | 13 import android.os.Handler; |
14 import android.os.IBinder; | 14 import android.os.IBinder; |
15 import android.os.Message; | 15 import android.os.Message; |
16 import android.os.Messenger; | 16 import android.os.Messenger; |
17 import android.os.ParcelFileDescriptor; | 17 import android.os.ParcelFileDescriptor; |
18 import android.os.RemoteException; | 18 import android.os.RemoteException; |
19 import android.os.StrictMode; | 19 import android.os.StrictMode; |
| 20 import android.os.SystemClock; |
20 | 21 |
21 import org.chromium.base.Log; | 22 import org.chromium.base.Log; |
| 23 import org.chromium.base.metrics.RecordHistogram; |
| 24 import org.chromium.chrome.browser.util.ConversionUtils; |
22 | 25 |
23 import java.io.File; | 26 import java.io.File; |
24 import java.io.FileDescriptor; | 27 import java.io.FileDescriptor; |
25 import java.io.FileInputStream; | 28 import java.io.FileInputStream; |
26 import java.io.IOException; | 29 import java.io.IOException; |
27 import java.lang.ref.WeakReference; | 30 import java.lang.ref.WeakReference; |
28 import java.util.LinkedHashMap; | 31 import java.util.LinkedHashMap; |
| 32 import java.util.concurrent.TimeUnit; |
29 | 33 |
30 /** | 34 /** |
31 * A class to communicate with the {@link DecoderService}. | 35 * A class to communicate with the {@link DecoderService}. |
32 */ | 36 */ |
33 public class DecoderServiceHost { | 37 public class DecoderServiceHost { |
34 // A tag for logging error messages. | 38 // A tag for logging error messages. |
35 private static final String TAG = "ImageDecoderHost"; | 39 private static final String TAG = "ImageDecoderHost"; |
36 | 40 |
37 /** | 41 /** |
38 * Interface for notifying clients of the service being ready. | 42 * Interface for notifying clients of the service being ready. |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 private static class DecoderServiceParams { | 90 private static class DecoderServiceParams { |
87 // The path to the file containing the bitmap to decode. | 91 // The path to the file containing the bitmap to decode. |
88 public String mFilePath; | 92 public String mFilePath; |
89 | 93 |
90 // The requested size (width and height) of the bitmap, once decoded. | 94 // The requested size (width and height) of the bitmap, once decoded. |
91 public int mSize; | 95 public int mSize; |
92 | 96 |
93 // The callback to use to communicate the results of the decoding. | 97 // The callback to use to communicate the results of the decoding. |
94 ImageDecodedCallback mCallback; | 98 ImageDecodedCallback mCallback; |
95 | 99 |
| 100 // The timestamp for when the request was sent for decoding. |
| 101 long mTimestamp; |
| 102 |
96 public DecoderServiceParams(String filePath, int size, ImageDecodedCallb
ack callback) { | 103 public DecoderServiceParams(String filePath, int size, ImageDecodedCallb
ack callback) { |
97 mFilePath = filePath; | 104 mFilePath = filePath; |
98 mSize = size; | 105 mSize = size; |
99 mCallback = callback; | 106 mCallback = callback; |
100 } | 107 } |
101 } | 108 } |
102 | 109 |
103 // Map of file paths to decoder parameters in order of request. | 110 // Map of file paths to decoder parameters in order of request. |
104 private LinkedHashMap<String, DecoderServiceParams> mRequests = new LinkedHa
shMap<>(); | 111 private LinkedHashMap<String, DecoderServiceParams> mRequests = new LinkedHa
shMap<>(); |
105 LinkedHashMap<String, DecoderServiceParams> getRequests() { | 112 LinkedHashMap<String, DecoderServiceParams> getRequests() { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 mRequests.put(filePath, params); | 169 mRequests.put(filePath, params); |
163 if (mRequests.size() == 1) dispatchNextDecodeImageRequest(); | 170 if (mRequests.size() == 1) dispatchNextDecodeImageRequest(); |
164 } | 171 } |
165 | 172 |
166 /** | 173 /** |
167 * Dispatches the next image for decoding (from the queue). | 174 * Dispatches the next image for decoding (from the queue). |
168 */ | 175 */ |
169 private void dispatchNextDecodeImageRequest() { | 176 private void dispatchNextDecodeImageRequest() { |
170 if (mRequests.entrySet().iterator().hasNext()) { | 177 if (mRequests.entrySet().iterator().hasNext()) { |
171 DecoderServiceParams params = mRequests.entrySet().iterator().next()
.getValue(); | 178 DecoderServiceParams params = mRequests.entrySet().iterator().next()
.getValue(); |
| 179 params.mTimestamp = SystemClock.elapsedRealtime(); |
172 dispatchDecodeImageRequest(params.mFilePath, params.mSize); | 180 dispatchDecodeImageRequest(params.mFilePath, params.mSize); |
173 } | 181 } |
174 } | 182 } |
175 | 183 |
176 /** | 184 /** |
177 * Ties up all the loose ends from the decoding request (communicates the re
sults of the | 185 * Ties up all the loose ends from the decoding request (communicates the re
sults of the |
178 * decoding process back to the client, and takes care of house-keeping chor
es regarding | 186 * decoding process back to the client, and takes care of house-keeping chor
es regarding |
179 * the request queue). | 187 * the request queue). |
180 * @param filePath The path to the image that was just decoded. | 188 * @param filePath The path to the image that was just decoded. |
181 * @param bitmap The resulting decoded bitmap. | 189 * @param bitmap The resulting decoded bitmap. |
| 190 * @param decodeTime The length of time it took to decode the bitmap. |
182 */ | 191 */ |
183 public void closeRequest(String filePath, Bitmap bitmap) { | 192 public void closeRequest(String filePath, Bitmap bitmap, long decodeTime) { |
184 DecoderServiceParams params = getRequests().get(filePath); | 193 DecoderServiceParams params = getRequests().get(filePath); |
185 if (params != null) { | 194 if (params != null) { |
| 195 long endRpcCall = SystemClock.elapsedRealtime(); |
| 196 RecordHistogram.recordTimesHistogram("Android.PhotoPicker.RequestPro
cessTime", |
| 197 endRpcCall - params.mTimestamp, TimeUnit.MILLISECONDS); |
| 198 |
186 params.mCallback.imageDecodedCallback(filePath, bitmap); | 199 params.mCallback.imageDecodedCallback(filePath, bitmap); |
| 200 |
| 201 if (decodeTime != -1) { |
| 202 RecordHistogram.recordTimesHistogram( |
| 203 "Android.PhotoPicker.ImageDecodeTime", decodeTime, TimeU
nit.MILLISECONDS); |
| 204 |
| 205 int sizeInKB = bitmap.getByteCount() / ConversionUtils.BYTES_PER
_KILOBYTE; |
| 206 RecordHistogram.recordCustomCountHistogram( |
| 207 "Android.PhotoPicker.ImageByteCount", sizeInKB, 1, 10000
0, 50); |
| 208 } |
187 getRequests().remove(filePath); | 209 getRequests().remove(filePath); |
188 } | 210 } |
189 dispatchNextDecodeImageRequest(); | 211 dispatchNextDecodeImageRequest(); |
190 } | 212 } |
191 | 213 |
192 /** | 214 /** |
193 * Communicates with the server to decode a single bitmap. | 215 * Communicates with the server to decode a single bitmap. |
194 * @param filePath The path to the image on disk. | 216 * @param filePath The path to the image on disk. |
195 * @param size The requested width and height of the resulting bitmap. | 217 * @param size The requested width and height of the resulting bitmap. |
196 */ | 218 */ |
197 private void dispatchDecodeImageRequest(String filePath, int size) { | 219 private void dispatchDecodeImageRequest(String filePath, int size) { |
198 // Obtain a file descriptor to send over to the sandboxed process. | 220 // Obtain a file descriptor to send over to the sandboxed process. |
199 File file = new File(filePath); | 221 File file = new File(filePath); |
200 FileInputStream inputFile = null; | 222 FileInputStream inputFile = null; |
201 ParcelFileDescriptor pfd = null; | 223 ParcelFileDescriptor pfd = null; |
202 Bundle bundle = new Bundle(); | 224 Bundle bundle = new Bundle(); |
203 | 225 |
204 // The restricted utility process can't open the file to read the | 226 // The restricted utility process can't open the file to read the |
205 // contents, so we need to obtain a file descriptor to pass over. | 227 // contents, so we need to obtain a file descriptor to pass over. |
206 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); | 228 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); |
207 try { | 229 try { |
208 try { | 230 try { |
209 inputFile = new FileInputStream(file); | 231 inputFile = new FileInputStream(file); |
210 FileDescriptor fd = inputFile.getFD(); | 232 FileDescriptor fd = inputFile.getFD(); |
211 pfd = ParcelFileDescriptor.dup(fd); | 233 pfd = ParcelFileDescriptor.dup(fd); |
212 bundle.putParcelable(DecoderService.KEY_FILE_DESCRIPTOR, pfd); | 234 bundle.putParcelable(DecoderService.KEY_FILE_DESCRIPTOR, pfd); |
213 } catch (IOException e) { | 235 } catch (IOException e) { |
214 Log.e(TAG, "Unable to obtain FileDescriptor: " + e); | 236 Log.e(TAG, "Unable to obtain FileDescriptor: " + e); |
215 closeRequest(filePath, null); | 237 closeRequest(filePath, null, -1); |
216 } | 238 } |
217 } finally { | 239 } finally { |
218 try { | 240 try { |
219 if (inputFile != null) inputFile.close(); | 241 if (inputFile != null) inputFile.close(); |
220 } catch (IOException e) { | 242 } catch (IOException e) { |
221 Log.e(TAG, "Unable to close inputFile: " + e); | 243 Log.e(TAG, "Unable to close inputFile: " + e); |
222 } | 244 } |
223 StrictMode.setThreadPolicy(oldPolicy); | 245 StrictMode.setThreadPolicy(oldPolicy); |
224 } | 246 } |
225 | 247 |
226 if (pfd == null) return; | 248 if (pfd == null) return; |
227 | 249 |
228 // Prepare and send the data over. | 250 // Prepare and send the data over. |
229 Message payload = Message.obtain(null, DecoderService.MSG_DECODE_IMAGE); | 251 Message payload = Message.obtain(null, DecoderService.MSG_DECODE_IMAGE); |
230 payload.replyTo = mMessenger; | 252 payload.replyTo = mMessenger; |
231 bundle.putString(DecoderService.KEY_FILE_PATH, filePath); | 253 bundle.putString(DecoderService.KEY_FILE_PATH, filePath); |
232 bundle.putInt(DecoderService.KEY_SIZE, size); | 254 bundle.putInt(DecoderService.KEY_SIZE, size); |
233 payload.setData(bundle); | 255 payload.setData(bundle); |
234 try { | 256 try { |
235 mService.send(payload); | 257 mService.send(payload); |
236 pfd.close(); | 258 pfd.close(); |
237 } catch (RemoteException e) { | 259 } catch (RemoteException e) { |
238 Log.e(TAG, "Communications failed (Remote): " + e); | 260 Log.e(TAG, "Communications failed (Remote): " + e); |
239 closeRequest(filePath, null); | 261 closeRequest(filePath, null, -1); |
240 } catch (IOException e) { | 262 } catch (IOException e) { |
241 Log.e(TAG, "Communications failed (IO): " + e); | 263 Log.e(TAG, "Communications failed (IO): " + e); |
242 closeRequest(filePath, null); | 264 closeRequest(filePath, null, -1); |
243 } | 265 } |
244 } | 266 } |
245 | 267 |
246 /** | 268 /** |
247 * Cancels a request to decode an image (if it hasn't already been dispatche
d). | 269 * Cancels a request to decode an image (if it hasn't already been dispatche
d). |
248 * @param filePath The path to the image to cancel decoding. | 270 * @param filePath The path to the image to cancel decoding. |
249 */ | 271 */ |
250 public void cancelDecodeImage(String filePath) { | 272 public void cancelDecodeImage(String filePath) { |
251 mRequests.remove(filePath); | 273 mRequests.remove(filePath); |
252 } | 274 } |
(...skipping 24 matching lines...) Expand all Loading... |
277 switch (msg.what) { | 299 switch (msg.what) { |
278 case DecoderService.MSG_IMAGE_DECODED_REPLY: | 300 case DecoderService.MSG_IMAGE_DECODED_REPLY: |
279 Bundle payload = msg.getData(); | 301 Bundle payload = msg.getData(); |
280 | 302 |
281 // Read the reply back from the service. | 303 // Read the reply back from the service. |
282 String filePath = payload.getString(DecoderService.KEY_FILE_
PATH); | 304 String filePath = payload.getString(DecoderService.KEY_FILE_
PATH); |
283 Boolean success = payload.getBoolean(DecoderService.KEY_SUCC
ESS); | 305 Boolean success = payload.getBoolean(DecoderService.KEY_SUCC
ESS); |
284 Bitmap bitmap = success | 306 Bitmap bitmap = success |
285 ? (Bitmap) payload.getParcelable(DecoderService.KEY_
IMAGE_BITMAP) | 307 ? (Bitmap) payload.getParcelable(DecoderService.KEY_
IMAGE_BITMAP) |
286 : null; | 308 : null; |
287 host.closeRequest(filePath, bitmap); | 309 long decodeTime = payload.getLong(DecoderService.KEY_DECODE_
TIME); |
| 310 host.closeRequest(filePath, bitmap, decodeTime); |
288 break; | 311 break; |
289 default: | 312 default: |
290 super.handleMessage(msg); | 313 super.handleMessage(msg); |
291 } | 314 } |
292 } | 315 } |
293 } | 316 } |
294 } | 317 } |
OLD | NEW |