blob: ef8845849f301974b2c0ad6f84a37e2233eb129d [file] [log] [blame]
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -07001/*
Kevin Hart60a37f72015-02-09 17:14:14 -08002 * Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -07003 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31package com.android.browser;
32
33import android.content.Context;
34import android.content.Intent;
35import android.os.AsyncTask;
36import android.os.Handler;
37import android.os.Looper;
Tarun Nainania1d74d62015-01-07 12:40:09 -080038import android.os.StrictMode;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070039import android.util.Log;
40
Tarun Nainania1d74d62015-01-07 12:40:09 -080041import org.codeaurora.swe.BrowserCommandLine;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070042import org.codeaurora.swe.Engine;
43
44import java.util.ArrayList;
45import java.util.concurrent.CancellationException;
46import java.util.concurrent.ExecutionException;
47
Vivek Sekhared791da2015-02-22 12:39:05 -080048import org.chromium.base.VisibleForTesting;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070049
50public class EngineInitializer {
51 private final static String LOGTAG = "EngineInitializer";
52
53 private BrowserActivity mActivity;
54
55 private boolean mNotifyActivity = false;
56 private boolean mActivityReady = false;
57 private boolean mActivityDestroyed = false;
58 private boolean mActivityStartPending = false;
59 private boolean mOnResumePending = false;
60
61 private boolean mFirstDrawCompleted = false;
62 private boolean mLibraryLoaded = false;
63 private boolean mInitializationCompleted = false;
64
65 private Handler mUiThreadHandler;
66
67 class ActivityResult
68 {
69 public Intent data;
70 public int requestCode;
71 public int resultCode;
72
73 public ActivityResult(int requestCode, int resultCode, Intent data)
74 {
75 this.requestCode = requestCode;
76 this.resultCode = resultCode;
77 this.data = data;
78 }
79 }
80 private ArrayList<ActivityResult> mPendingActivityResults = null;
81 private ArrayList<Intent> mPendingIntents = null;
82
83 private static EngineInitializer sEngineInitializer = null;
84 public static EngineInitializer getInstance() {
85 if (sEngineInitializer == null) {
86 sEngineInitializer = new EngineInitializer();
87 }
88 return sEngineInitializer;
89 }
90
91 private static long sDelayForTesting = 0;
92
Tarun Nainania1d74d62015-01-07 12:40:09 -080093 //Command line flag for strict mode
94 private static final String STRICT_MODE = "enable-strict-mode";
95
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070096 @VisibleForTesting
97 public static void setDelayForTesting(long delay)
98 {
99 sDelayForTesting = delay;
100 }
101
102 private EngineInitializer() {
103 mUiThreadHandler = new Handler(Looper.getMainLooper());
104 }
105
106 @VisibleForTesting
107 public boolean isInitialized()
108 {
109 return mInitializationCompleted;
110 }
111
112 public boolean runningOnUiThread() {
113 return mUiThreadHandler.getLooper() == Looper.myLooper();
114 }
115
116 public void postOnUiThread(Runnable task) {
117 mUiThreadHandler.post(task);
118 }
119
120 private class InitializeTask extends AsyncTask<Void, Void, Boolean> {
121 public InitializeTask() {
122 }
123 @Override
124 protected Boolean doInBackground(Void... unused) {
125 try
126 {
127 // For testing.
128 if (sDelayForTesting > 0) {
129 Thread.sleep(sDelayForTesting);
130 }
131
132 Engine.loadNativeLibraries(mActivity.getApplicationContext());
133
134 Engine.warmUpChildProcess(mActivity.getApplicationContext());
135
136 return true;
137 }
138 catch (Exception e)
139 {
140 Log.e(LOGTAG, "Unable to load native library.", e);
141 }
142 return false;
143 }
144
145 @Override
146 protected void onPostExecute (Boolean result) {
147 mLibraryLoaded = true;
148 if (mFirstDrawCompleted) {
149 completeInitializationOnUiThread(mActivity.getApplicationContext());
150 }
151 }
152 }
153 private InitializeTask mInitializeTask = null;
154
155 public void initializeSync(Context ctx) {
156 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
157
158 if (mInitializeTask != null) {
159 try {
160 // Wait for the InitializeTask to finish.
161 mInitializeTask.get();
162 } catch (CancellationException e1) {
163 Log.e(LOGTAG, "Native library load cancelled", e1);
164 } catch (ExecutionException e2) {
165 Log.e(LOGTAG, "Native library load failed", e2);
166 } catch (InterruptedException e3) {
167 Log.e(LOGTAG, "Native library load interrupted", e3);
168 }
169 }
170 completeInitializationOnUiThread(ctx);
171 }
172
173 private void reset(BrowserActivity newActivity) {
174 mActivity = newActivity;
175 mActivityStartPending = false;
176 mOnResumePending = false;
177 mNotifyActivity = true;
178 mActivityReady = false;
179 mPendingIntents = null;
180 mPendingActivityResults = null;
181 mFirstDrawCompleted = false;
182 mActivityDestroyed = false;
183 }
184
185 public void onActivityCreate(BrowserActivity activity) {
186 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
187 reset(activity);
188 if (!mInitializationCompleted) {
189 Engine.initializeCommandLine(mActivity.getApplicationContext());
190 mInitializeTask = new InitializeTask();
191 mInitializeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
192 }
193 }
194
195 private void completeInitialization() {
196 postOnUiThread(new Runnable() {
197 @Override
198 public void run() {
199 completeInitializationOnUiThread(mActivity.getApplicationContext());
200 }
201 });
202 }
203
204 private void completeInitializationOnUiThread(Context ctx) {
205 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
206
207 if (!mInitializationCompleted) {
208 // TODO: Evaluate the benefit of async Engine.initialize()
209 Engine.initialize(ctx);
Vivek Sekhardcf9d6b2014-12-01 15:08:37 -0800210 // Add the browser commandline options
211 BrowserConfig.getInstance(ctx).initCommandLineSwitches();
Tarun Nainania1d74d62015-01-07 12:40:09 -0800212
213 //Note: Only enable this for debugging.
214 if (BrowserCommandLine.hasSwitch(STRICT_MODE)) {
215 Log.v(LOGTAG, "StrictMode enabled");
216 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
217 .detectDiskReads()
218 .detectDiskWrites()
219 .detectNetwork()
220 .penaltyLog()
221 .build());
222 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
223 .detectLeakedSqlLiteObjects()
224 .detectLeakedClosableObjects()
225 .penaltyLog()
226 .penaltyDeath()
227 .build());
228 }
229
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700230 //Enable remote debugging by default
231 Engine.setWebContentsDebuggingEnabled(true);
232 mInitializationCompleted = true;
233 mLibraryLoaded = true;
234 BrowserSettings.getInstance().onEngineInitializationComplete();
235 }
236 if (mActivity != null && mNotifyActivity) {
237 mNotifyActivity = false;
238 postOnUiThread(new Runnable() {
239 @Override
240 public void run() {
241 mActivity.onEngineInitializationComplete();
242 mActivityReady = true;
243 processPendingEvents();
244 }
245 });
246 }
247
248 }
249
Vivek Sekhared791da2015-02-22 12:39:05 -0800250 private void completeInitializationAsynOnUiThread(final Context ctx) {
251 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
252
253 if (!mInitializationCompleted) {
254 // TODO: Evaluate the benefit of async Engine.initialize()
255 Engine.StartupCallback callback =
256 new Engine.StartupCallback() {
257 @Override
258 public void onSuccess(boolean alreadyStarted) {
259 if (Looper.myLooper() == Looper.getMainLooper()) {
260 Log.e(LOGTAG, "SWE engine initialization success");
261 // Add the browser commandline options
262 BrowserConfig.getInstance(ctx).initCommandLineSwitches();
263
264 //Note: Only enable this for debugging.
265 if (BrowserCommandLine.hasSwitch(STRICT_MODE)) {
266 Log.v(LOGTAG, "StrictMode enabled");
267 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
268 .detectDiskReads()
269 .detectDiskWrites()
270 .detectNetwork()
271 .penaltyLog()
272 .build());
273 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
274 .detectLeakedSqlLiteObjects()
275 .detectLeakedClosableObjects()
276 .penaltyLog()
277 .penaltyDeath()
278 .build());
279 }
280
281 //Enable remote debugging by default
282 Engine.setWebContentsDebuggingEnabled(true);
283 mInitializationCompleted = true;
284 mLibraryLoaded = true;
285 BrowserSettings.getInstance().onEngineInitializationComplete();
286
287 if (mActivity != null && mNotifyActivity) {
288 mNotifyActivity = false;
289 postOnUiThread(new Runnable() {
290 @Override
291 public void run() {
292 mActivity.onEngineInitializationComplete();
293 mActivityReady = true;
294 processPendingEvents();
295 }
296 });
297 }
298 }
299 }
300
301 @Override
302 public void onFailure() {
303 Log.e(LOGTAG, "SWE engine initialization failed");
304 }
305 };
306 Engine.initialize(ctx, callback);
307 }
308 }
309
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700310 private void processPendingEvents() {
311 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
312
313 if (mActivityStartPending) {
314 mActivityStartPending = false;
315 onActivityStart();
316 }
317 if (mPendingIntents != null) {
318 for (int i = 0; i < mPendingIntents.size(); i++) {
319 mActivity.handleOnNewIntent(mPendingIntents.get(i));
320 }
321 mPendingIntents = null;
322 }
323 if (mPendingActivityResults != null) {
324 for (int i = 0; i < mPendingActivityResults.size(); i++) {
325 ActivityResult result = mPendingActivityResults.get(i);
326 mActivity.handleOnActivityResult(result.requestCode, result.resultCode, result.data);
327 }
328 mPendingActivityResults = null;
329 }
330 if (mOnResumePending && !mActivityDestroyed) {
331 onActivityResume();
332 }
333 mOnResumePending = false;
334 }
335
336 public void onPreDraw() {
337 mFirstDrawCompleted = true;
338 if (mLibraryLoaded) {
339 completeInitialization();
340 }
341 }
342
343 public void initializeResourceExtractor(Context ctx) {
344 Engine.startExtractingResources(ctx);
345 }
346
347 public void onActivityPause() {
348 mOnResumePending = false;
349 if (mActivityReady) {
Vivek Sekhar2868b8d2014-12-03 17:22:50 -0800350 mActivity.handleOnPause();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700351 }
352 }
353
Tarun Nainanib4a173f2015-01-05 09:23:08 -0800354 public void onActivityStop() {
355 mActivityStartPending = false;
356 if (mActivityReady) {
357 Engine.pauseTracing(mActivity.getApplicationContext());
358 mActivity.handleOnStop();
359 }
360 }
361
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700362 public void onActivityResume() {
363 if (mActivityReady) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700364 mActivity.handleOnResume();
365 return;
366 }
367 mOnResumePending = true;
368 }
369
370 public void onActivityStart() {
371 if (mActivityReady) {
Tarun Nainanib4a173f2015-01-05 09:23:08 -0800372 Engine.resumeTracing(mActivity.getApplicationContext());
373 mActivity.handleOnStart();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700374 // TODO: We have no reliable mechanism to know when the app goes background.
375 //ChildProcessLauncher.onBroughtToForeground();
376 return;
377 }
378 mActivityStartPending = true;
379 }
380
381 public void onActivityResult(int requestCode, int resultCode, Intent data) {
382 if (mActivityReady) {
383 mActivity.handleOnActivityResult(requestCode, resultCode, data);
384 return;
385 }
386 if (mPendingActivityResults == null) {
387 mPendingActivityResults = new ArrayList<ActivityResult>(1);
388 }
389 mPendingActivityResults.add(new ActivityResult(requestCode, resultCode, data));
390 }
391
392 public void onNewIntent(Intent intent) {
Kevin Hart60a37f72015-02-09 17:14:14 -0800393 if (BrowserActivity.ACTION_RESTART.equals(intent.getAction())) {
394 Engine.releaseSpareChildProcess();
395 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700396 if (mActivityReady) {
397 mActivity.handleOnNewIntent(intent);
398 return;
399 }
400
401 if (mPendingIntents == null) {
402 mPendingIntents = new ArrayList<Intent>(1);
403 }
404 mPendingIntents.add(intent);
405 }
406
407 public void onActivityDestroy() {
Kevin Hart4ad2edc2015-01-19 15:00:21 -0800408 Engine.releaseSpareChildProcess();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700409 mActivityDestroyed = true;
410 }
411
412
Tarun Nainania1d74d62015-01-07 12:40:09 -0800413}