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; | |
14 import android.os.IBinder; | 13 import android.os.IBinder; |
15 import android.os.Message; | 14 import android.os.Message; |
16 import android.os.Messenger; | |
17 import android.os.ParcelFileDescriptor; | 15 import android.os.ParcelFileDescriptor; |
18 import android.os.RemoteException; | 16 import android.os.RemoteException; |
19 import android.os.StrictMode; | 17 import android.os.StrictMode; |
20 import android.os.SystemClock; | 18 import android.os.SystemClock; |
21 | 19 |
22 import org.chromium.base.Log; | 20 import org.chromium.base.Log; |
21 import org.chromium.base.ThreadUtils; | |
23 import org.chromium.base.metrics.RecordHistogram; | 22 import org.chromium.base.metrics.RecordHistogram; |
24 import org.chromium.chrome.browser.util.ConversionUtils; | 23 import org.chromium.chrome.browser.util.ConversionUtils; |
25 | 24 |
26 import java.io.File; | 25 import java.io.File; |
27 import java.io.FileDescriptor; | 26 import java.io.FileDescriptor; |
28 import java.io.FileInputStream; | 27 import java.io.FileInputStream; |
29 import java.io.IOException; | 28 import java.io.IOException; |
30 import java.lang.ref.WeakReference; | |
31 import java.util.LinkedHashMap; | 29 import java.util.LinkedHashMap; |
32 import java.util.concurrent.TimeUnit; | 30 import java.util.concurrent.TimeUnit; |
33 | 31 |
34 /** | 32 /** |
35 * A class to communicate with the {@link DecoderService}. | 33 * A class to communicate with the {@link DecoderServiceImpl}. |
36 */ | 34 */ |
37 public class DecoderServiceHost { | 35 public class DecoderServiceHost extends IDecoderServiceListener.Stub { |
38 // A tag for logging error messages. | 36 // A tag for logging error messages. |
39 private static final String TAG = "ImageDecoderHost"; | 37 private static final String TAG = "ImageDecoderHost"; |
40 | 38 |
39 IDecoderService mIRemoteService; | |
40 private ServiceConnection mConnection = new ServiceConnection() { | |
41 public void onServiceConnected(ComponentName className, IBinder service) { | |
42 mIRemoteService = IDecoderService.Stub.asInterface(service); | |
43 mBound = true; | |
44 mCallback.serviceReady(); | |
45 } | |
46 | |
47 public void onServiceDisconnected(ComponentName className) { | |
48 Log.e(TAG, "Service has unexpectedly disconnected"); | |
Robert Sesek
2017/06/20 02:09:47
This will be called if you ever unbindService(), w
Finnur
2017/06/20 12:20:57
I do call unbindService but I do not see this func
| |
49 mIRemoteService = null; | |
50 mBound = false; | |
51 } | |
52 }; | |
53 | |
41 /** | 54 /** |
42 * Interface for notifying clients of the service being ready. | 55 * Interface for notifying clients of the service being ready. |
43 */ | 56 */ |
44 public interface ServiceReadyCallback { | 57 public interface ServiceReadyCallback { |
45 /** | 58 /** |
46 * A function to define to receive a notification once the service is up and running. | 59 * A function to define to receive a notification once the service is up and running. |
47 */ | 60 */ |
48 void serviceReady(); | 61 void serviceReady(); |
49 } | 62 } |
50 | 63 |
51 /** | 64 /** |
52 * An interface notifying clients when an image has finished decoding. | 65 * An interface notifying clients when an image has finished decoding. |
53 */ | 66 */ |
54 public interface ImageDecodedCallback { | 67 public interface ImageDecodedCallback { |
55 /** | 68 /** |
56 * A function to define to receive a notification that an image has been decoded. | 69 * A function to define to receive a notification that an image has been decoded. |
57 * @param filePath The file path for the newly decoded image. | 70 * @param filePath The file path for the newly decoded image. |
58 * @param bitmap The results of the decoding (or placeholder image, if f ailed). | 71 * @param bitmap The results of the decoding (or placeholder image, if f ailed). |
59 */ | 72 */ |
60 void imageDecodedCallback(String filePath, Bitmap bitmap); | 73 void imageDecodedCallback(String filePath, Bitmap bitmap); |
61 } | 74 } |
62 | 75 |
63 /** | 76 /** |
64 * Class for interacting with the main interface of the service. | |
65 */ | |
66 private class DecoderServiceConnection implements ServiceConnection { | |
67 // The callback to use to notify the service being ready. | |
68 private ServiceReadyCallback mCallback; | |
69 | |
70 public DecoderServiceConnection(ServiceReadyCallback callback) { | |
71 mCallback = callback; | |
72 } | |
73 | |
74 // Called when a connection to the service has been established. | |
75 public void onServiceConnected(ComponentName name, IBinder service) { | |
76 mService = new Messenger(service); | |
77 mBound = true; | |
78 mCallback.serviceReady(); | |
79 } | |
80 | |
81 // Called when a connection to the service has been lost. | |
82 public void onServiceDisconnected(ComponentName name) { | |
83 mBound = false; | |
84 } | |
85 } | |
86 | |
87 /** | |
88 * Class for keeping track of the data involved with each request. | 77 * Class for keeping track of the data involved with each request. |
89 */ | 78 */ |
90 private static class DecoderServiceParams { | 79 private static class DecoderServiceParams { |
91 // The path to the file containing the bitmap to decode. | 80 // The path to the file containing the bitmap to decode. |
92 public String mFilePath; | 81 public String mFilePath; |
93 | 82 |
94 // The requested size (width and height) of the bitmap, once decoded. | 83 // The requested size (width and height) of the bitmap, once decoded. |
95 public int mSize; | 84 public int mSize; |
96 | 85 |
97 // The callback to use to communicate the results of the decoding. | 86 // The callback to use to communicate the results of the decoding. |
(...skipping 11 matching lines...) Expand all Loading... | |
109 | 98 |
110 // Map of file paths to decoder parameters in order of request. | 99 // Map of file paths to decoder parameters in order of request. |
111 private LinkedHashMap<String, DecoderServiceParams> mRequests = new LinkedHa shMap<>(); | 100 private LinkedHashMap<String, DecoderServiceParams> mRequests = new LinkedHa shMap<>(); |
112 LinkedHashMap<String, DecoderServiceParams> getRequests() { | 101 LinkedHashMap<String, DecoderServiceParams> getRequests() { |
113 return mRequests; | 102 return mRequests; |
114 } | 103 } |
115 | 104 |
116 // The callback used to notify the client when the service is ready. | 105 // The callback used to notify the client when the service is ready. |
117 private ServiceReadyCallback mCallback; | 106 private ServiceReadyCallback mCallback; |
118 | 107 |
119 // Messenger for communicating with the remote service. | |
120 Messenger mService = null; | |
121 | |
122 // Our service connection to the {@link DecoderService}. | |
123 private DecoderServiceConnection mConnection; | |
124 | |
125 // Flag indicating whether we are bound to the service. | 108 // Flag indicating whether we are bound to the service. |
126 boolean mBound; | 109 boolean mBound; |
127 | 110 |
128 // The inbound messenger used by the remote service to communicate with us. | 111 private Context mContext; |
129 final Messenger mMessenger = new Messenger(new IncomingHandler(this)); | |
130 | 112 |
131 /** | 113 /** |
132 * The DecoderServiceHost constructor. | 114 * The DecoderServiceHost constructor. |
133 * @param callback The callback to use when communicating back to the client . | 115 * @param callback The callback to use when communicating back to the client . |
134 */ | 116 */ |
135 public DecoderServiceHost(ServiceReadyCallback callback) { | 117 public DecoderServiceHost(ServiceReadyCallback callback, Context context) { |
136 mCallback = callback; | 118 mCallback = callback; |
119 mContext = context; | |
137 } | 120 } |
138 | 121 |
139 /** | 122 /** |
140 * Initiate binding with the {@link DecoderService}. | 123 * Initiate binding with the {@link DecoderServiceImpl}. |
141 * @param context The context to use. | 124 * @param context The context to use. |
142 */ | 125 */ |
143 public void bind(Context context) { | 126 public void bind(Context context) { |
144 mConnection = new DecoderServiceConnection(mCallback); | 127 Intent intent = new Intent(mContext, DecoderServiceImpl.class); |
145 Intent intent = new Intent(context, DecoderService.class); | 128 intent.setAction(IDecoderService.class.getName()); |
146 context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); | 129 mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); |
147 } | 130 } |
148 | 131 |
149 /** | 132 /** |
150 * Unbind from the {@link DecoderService}. | 133 * Unbind from the {@link DecoderServiceImpl}. |
151 * @param context The context to use. | 134 * @param context The context to use. |
152 */ | 135 */ |
153 public void unbind(Context context) { | 136 public void unbind(Context context) { |
154 if (mBound) { | 137 if (mBound) { |
155 context.unbindService(mConnection); | 138 context.unbindService(mConnection); |
156 mBound = false; | 139 mBound = false; |
157 } | 140 } |
158 } | 141 } |
159 | 142 |
160 /** | 143 /** |
(...skipping 13 matching lines...) Expand all Loading... | |
174 * Dispatches the next image for decoding (from the queue). | 157 * Dispatches the next image for decoding (from the queue). |
175 */ | 158 */ |
176 private void dispatchNextDecodeImageRequest() { | 159 private void dispatchNextDecodeImageRequest() { |
177 if (mRequests.entrySet().iterator().hasNext()) { | 160 if (mRequests.entrySet().iterator().hasNext()) { |
178 DecoderServiceParams params = mRequests.entrySet().iterator().next() .getValue(); | 161 DecoderServiceParams params = mRequests.entrySet().iterator().next() .getValue(); |
179 params.mTimestamp = SystemClock.elapsedRealtime(); | 162 params.mTimestamp = SystemClock.elapsedRealtime(); |
180 dispatchDecodeImageRequest(params.mFilePath, params.mSize); | 163 dispatchDecodeImageRequest(params.mFilePath, params.mSize); |
181 } | 164 } |
182 } | 165 } |
183 | 166 |
167 @Override | |
168 public void onDecodeImageDone(final Bundle payload) { | |
169 ThreadUtils.runOnUiThread(new Runnable() { | |
170 @Override | |
171 public void run() { | |
172 // Read the reply back from the service. | |
173 String filePath = payload.getString(DecoderServiceImpl.KEY_FILE_ PATH); | |
174 Boolean success = payload.getBoolean(DecoderServiceImpl.KEY_SUCC ESS); | |
175 Bitmap bitmap = success | |
176 ? (Bitmap) payload.getParcelable(DecoderServiceImpl.KEY_ IMAGE_BITMAP) | |
177 : null; | |
178 long decodeTime = payload.getLong(DecoderServiceImpl.KEY_DECODE_ TIME); | |
179 closeRequest(filePath, bitmap, decodeTime); | |
180 } | |
181 }); | |
182 } | |
183 | |
184 /** | 184 /** |
185 * 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 |
186 * 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 |
187 * the request queue). | 187 * the request queue). |
188 * @param filePath The path to the image that was just decoded. | 188 * @param filePath The path to the image that was just decoded. |
189 * @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. | 190 * @param decodeTime The length of time it took to decode the bitmap. |
191 */ | 191 */ |
192 public void closeRequest(String filePath, Bitmap bitmap, long decodeTime) { | 192 public void closeRequest(String filePath, Bitmap bitmap, long decodeTime) { |
193 DecoderServiceParams params = getRequests().get(filePath); | 193 DecoderServiceParams params = getRequests().get(filePath); |
(...skipping 30 matching lines...) Expand all Loading... | |
224 Bundle bundle = new Bundle(); | 224 Bundle bundle = new Bundle(); |
225 | 225 |
226 // 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 |
227 // 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. |
228 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); | 228 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); |
229 try { | 229 try { |
230 try { | 230 try { |
231 inputFile = new FileInputStream(file); | 231 inputFile = new FileInputStream(file); |
232 FileDescriptor fd = inputFile.getFD(); | 232 FileDescriptor fd = inputFile.getFD(); |
233 pfd = ParcelFileDescriptor.dup(fd); | 233 pfd = ParcelFileDescriptor.dup(fd); |
234 bundle.putParcelable(DecoderService.KEY_FILE_DESCRIPTOR, pfd); | 234 bundle.putParcelable(DecoderServiceImpl.KEY_FILE_DESCRIPTOR, pfd ); |
235 } catch (IOException e) { | 235 } catch (IOException e) { |
236 Log.e(TAG, "Unable to obtain FileDescriptor: " + e); | 236 Log.e(TAG, "Unable to obtain FileDescriptor: " + e); |
237 closeRequest(filePath, null, -1); | 237 closeRequest(filePath, null, -1); |
238 } | 238 } |
239 } finally { | 239 } finally { |
240 try { | 240 try { |
241 if (inputFile != null) inputFile.close(); | 241 if (inputFile != null) inputFile.close(); |
242 } catch (IOException e) { | 242 } catch (IOException e) { |
243 Log.e(TAG, "Unable to close inputFile: " + e); | 243 Log.e(TAG, "Unable to close inputFile: " + e); |
244 } | 244 } |
245 StrictMode.setThreadPolicy(oldPolicy); | 245 StrictMode.setThreadPolicy(oldPolicy); |
246 } | 246 } |
247 | 247 |
248 if (pfd == null) return; | 248 if (pfd == null) return; |
249 | 249 |
250 // Prepare and send the data over. | 250 // Prepare and send the data over. |
251 Message payload = Message.obtain(null, DecoderService.MSG_DECODE_IMAGE); | 251 Message payload = Message.obtain(); |
252 payload.replyTo = mMessenger; | 252 bundle.putString(DecoderServiceImpl.KEY_FILE_PATH, filePath); |
253 bundle.putString(DecoderService.KEY_FILE_PATH, filePath); | 253 bundle.putInt(DecoderServiceImpl.KEY_SIZE, size); |
254 bundle.putInt(DecoderService.KEY_SIZE, size); | |
255 payload.setData(bundle); | 254 payload.setData(bundle); |
256 try { | 255 try { |
257 mService.send(payload); | 256 mIRemoteService.decodeImage(bundle, this); |
258 pfd.close(); | 257 pfd.close(); |
259 } catch (RemoteException e) { | 258 } catch (RemoteException e) { |
260 Log.e(TAG, "Communications failed (Remote): " + e); | 259 Log.e(TAG, "Communications failed (Remote): " + e); |
261 closeRequest(filePath, null, -1); | 260 closeRequest(filePath, null, -1); |
262 } catch (IOException e) { | 261 } catch (IOException e) { |
263 Log.e(TAG, "Communications failed (IO): " + e); | 262 Log.e(TAG, "Communications failed (IO): " + e); |
264 closeRequest(filePath, null, -1); | 263 closeRequest(filePath, null, -1); |
265 } | 264 } |
266 } | 265 } |
267 | 266 |
268 /** | 267 /** |
269 * Cancels a request to decode an image (if it hasn't already been dispatche d). | 268 * Cancels a request to decode an image (if it hasn't already been dispatche d). |
270 * @param filePath The path to the image to cancel decoding. | 269 * @param filePath The path to the image to cancel decoding. |
271 */ | 270 */ |
272 public void cancelDecodeImage(String filePath) { | 271 public void cancelDecodeImage(String filePath) { |
273 mRequests.remove(filePath); | 272 mRequests.remove(filePath); |
274 } | 273 } |
275 | |
276 /** | |
277 * A class for handling communications from the service to us. | |
278 */ | |
279 static class IncomingHandler extends Handler { | |
280 // The DecoderServiceHost object to communicate with. | |
281 private final WeakReference<DecoderServiceHost> mHost; | |
282 | |
283 /** | |
284 * Constructor for IncomingHandler. | |
285 * @param host The DecoderServiceHost object to communicate with. | |
286 */ | |
287 IncomingHandler(DecoderServiceHost host) { | |
288 mHost = new WeakReference<DecoderServiceHost>(host); | |
289 } | |
290 | |
291 @Override | |
292 public void handleMessage(Message msg) { | |
293 DecoderServiceHost host = mHost.get(); | |
294 if (host == null) { | |
295 super.handleMessage(msg); | |
296 return; | |
297 } | |
298 | |
299 switch (msg.what) { | |
300 case DecoderService.MSG_IMAGE_DECODED_REPLY: | |
301 Bundle payload = msg.getData(); | |
302 | |
303 // Read the reply back from the service. | |
304 String filePath = payload.getString(DecoderService.KEY_FILE_ PATH); | |
305 Boolean success = payload.getBoolean(DecoderService.KEY_SUCC ESS); | |
306 Bitmap bitmap = success | |
307 ? (Bitmap) payload.getParcelable(DecoderService.KEY_ IMAGE_BITMAP) | |
308 : null; | |
309 long decodeTime = payload.getLong(DecoderService.KEY_DECODE_ TIME); | |
310 host.closeRequest(filePath, bitmap, decodeTime); | |
311 break; | |
312 default: | |
313 super.handleMessage(msg); | |
314 } | |
315 } | |
316 } | |
317 } | 274 } |
OLD | NEW |