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