blob: 037e65872dc78b4ce8af201e63ca26e69f9cd9c3 [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;
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070040import android.view.ViewTreeObserver;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070041
Tarun Nainania1d74d62015-01-07 12:40:09 -080042import org.codeaurora.swe.BrowserCommandLine;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070043import org.codeaurora.swe.Engine;
44
45import java.util.ArrayList;
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070046import java.util.HashMap;
47import java.util.Map;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070048import java.util.concurrent.CancellationException;
49import java.util.concurrent.ExecutionException;
50
Vivek Sekhared791da2015-02-22 12:39:05 -080051import org.chromium.base.VisibleForTesting;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070052
53public class EngineInitializer {
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070054
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070055 private final static String LOGTAG = "EngineInitializer";
Kevin Harta3d001d2015-04-28 11:35:30 -070056
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070057 private static boolean mInitializationStarted = false;
58 private static boolean mSynchronousInitialization = false;
59 private static boolean mInitializationCompleted = false;
60 private static Handler mUiThreadHandler;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070061
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070062 static class ActivityResult
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070063 {
64 public Intent data;
65 public int requestCode;
66 public int resultCode;
67
68 public ActivityResult(int requestCode, int resultCode, Intent data)
69 {
70 this.requestCode = requestCode;
71 this.resultCode = resultCode;
72 this.data = data;
73 }
74 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070075
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070076 public static class ActivityScheduler implements ViewTreeObserver.OnPreDrawListener
77 {
78 private BrowserActivity mActivity = null;
79 private ArrayList<ActivityResult> mPendingActivityResults = null;
80 private ArrayList<Intent> mPendingIntents = null;
81
82 private boolean mFirstDrawCompleted = false;
83 private boolean mOnStartPending = false;
84 private boolean mOnPausePending = false;
85 private boolean mEngineInitialized = false;
86 private boolean mCanForwardEvents = false;
87
88 public ActivityScheduler(BrowserActivity activity)
89 {
90 mActivity = activity;
91 mFirstDrawCompleted = false;
92 mOnStartPending = false;
93 mOnPausePending = false;
94 mPendingIntents = null;
95 mPendingActivityResults = null;
96 mEngineInitialized = false;
97 mCanForwardEvents = false;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070098 }
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070099
100 @VisibleForTesting
101 public boolean firstDrawCompleted() { return mFirstDrawCompleted; }
102 @VisibleForTesting
103 public boolean onStartPending() { return mOnStartPending; }
104 @VisibleForTesting
105 public boolean onPausePending() { return mOnPausePending; }
106 @VisibleForTesting
107 public boolean engineInitialized() { return mEngineInitialized; }
108 @VisibleForTesting
109 public boolean canForwardEvents() { return mCanForwardEvents; }
110
111 public void processPendingEvents() {
112 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
113
114 if (mOnStartPending) {
115 mOnStartPending = false;
116 mActivity.handleOnStart();
117 }
118 if (mOnPausePending) {
119 mActivity.handleOnPause();
120 mOnPausePending = false;
121 }
122 if (mPendingIntents != null) {
123 for (int i = 0; i < mPendingIntents.size(); i++) {
124 mActivity.handleOnNewIntent(mPendingIntents.get(i));
125 }
126 mPendingIntents = null;
127 }
128 if (mPendingActivityResults != null) {
129 for (int i = 0; i < mPendingActivityResults.size(); i++) {
130 ActivityResult result = mPendingActivityResults.get(i);
131 mActivity.handleOnActivityResult(result.requestCode, result.resultCode, result.data);
132 }
133 mPendingActivityResults = null;
134 }
135 mCanForwardEvents = true;
136 }
137
138 public void onActivityCreate(boolean engineInitialized) {
139 mEngineInitialized = engineInitialized;
140 if (!mEngineInitialized) {
141 // Engine initialization is not completed, we should wait for the onPreDraw() notification.
142 final ViewTreeObserver observer = mActivity.getWindow().getDecorView().getViewTreeObserver();
143 observer.addOnPreDrawListener(this);
144 } else {
145 mFirstDrawCompleted = true;
146 mCanForwardEvents = true;
147 }
148 }
149
150 @Override
151 public boolean onPreDraw() {
152 final ViewTreeObserver observer = mActivity.getWindow().getDecorView().getViewTreeObserver();
153 observer.removeOnPreDrawListener(this);
154
155 if (mFirstDrawCompleted)
156 return true;
157
158 mFirstDrawCompleted = true;
159 if (mEngineInitialized) {
160 postOnUiThread(new Runnable() {
161 @Override
162 public void run() {
163 mActivity.startController();
164 processPendingEvents();
165 }
166 });
167 }
168 return true;
169 }
170
171 public void onEngineInitializationCompletion(boolean synchronous) {
172 if (synchronous) {
173 // Don't wait for pre-draw notification if it is synchronous
174 onPreDraw();
175 }
176 mEngineInitialized = true;
177 if (mFirstDrawCompleted) {
178 mActivity.startController();
179 processPendingEvents();
180 }
181 }
182
183 public void onActivityPause() {
184 if (mCanForwardEvents) {
185 mActivity.handleOnPause();
186 return;
187 }
188 mOnPausePending = true;
189 }
190
191 public void onActivityResume() {
192 if (mCanForwardEvents) {
193 mActivity.handleOnResume();
194 return;
195 }
196 mOnPausePending = false;
197 }
198
199 public void onActivityStart() {
200 if (mCanForwardEvents) {
201 mActivity.handleOnStart();
202 // TODO: We have no reliable mechanism to know when the app goes background.
203 //ChildProcessLauncher.onBroughtToForeground();
204 return;
205 }
206 mOnStartPending = true;
207 }
208
209 public void onActivityStop() {
210 if (!mCanForwardEvents) {
211 initializeSync(mActivity.getApplicationContext());
212 }
213 mActivity.handleOnStop();
214 }
215
216 public void onActivityResult(int requestCode, int resultCode, Intent data) {
217 if (mCanForwardEvents) {
218 mActivity.handleOnActivityResult(requestCode, resultCode, data);
219 return;
220 }
221 if (mPendingActivityResults == null) {
222 mPendingActivityResults = new ArrayList<ActivityResult>(1);
223 }
224 mPendingActivityResults.add(new ActivityResult(requestCode, resultCode, data));
225 }
226
227 public void onNewIntent(Intent intent) {
228 if (mCanForwardEvents) {
229 mActivity.handleOnNewIntent(intent);
230 return;
231 }
232
233 if (mPendingIntents == null) {
234 mPendingIntents = new ArrayList<Intent>(1);
235 }
236 mPendingIntents.add(intent);
237 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700238 }
239
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700240 private static HashMap<BrowserActivity, ActivityScheduler> mActivitySchedulerMap = null;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700241 private static long sDelayForTesting = 0;
242
243 @VisibleForTesting
244 public static void setDelayForTesting(long delay)
245 {
246 sDelayForTesting = delay;
247 }
248
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700249 @VisibleForTesting
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700250 public static boolean isInitialized()
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700251 {
252 return mInitializationCompleted;
253 }
254
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700255 public static boolean runningOnUiThread() {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700256 return mUiThreadHandler.getLooper() == Looper.myLooper();
257 }
258
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700259 public static void postOnUiThread(Runnable task) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700260 mUiThreadHandler.post(task);
261 }
262
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700263 private static class InitializeTask extends AsyncTask<Void, Void, Boolean> {
264 private Context mApplicationContext;
265 public InitializeTask(Context ctx) {
266 mApplicationContext = ctx;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700267 }
268 @Override
269 protected Boolean doInBackground(Void... unused) {
270 try
271 {
272 // For testing.
273 if (sDelayForTesting > 0) {
274 Thread.sleep(sDelayForTesting);
275 }
276
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700277 Engine.loadNativeLibraries(mApplicationContext);
Tarun Nainanif03c7962015-06-03 11:38:33 -0700278 if (!BrowserCommandLine.hasSwitch(BrowserSwitches.SINGLE_PROCESS)) {
Kevin Harta3d001d2015-04-28 11:35:30 -0700279 Engine.warmUpChildProcess(mApplicationContext);
280 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700281 return true;
282 }
283 catch (Exception e)
284 {
285 Log.e(LOGTAG, "Unable to load native library.", e);
286 }
287 return false;
288 }
289
290 @Override
291 protected void onPostExecute (Boolean result) {
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700292 completeInitializationOnUiThread(mApplicationContext);
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700293 }
294 }
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700295 private static InitializeTask mInitializeTask = null;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700296
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700297 public static void initializeSync(Context ctx) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700298 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700299 mSynchronousInitialization = true;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700300 if (mInitializeTask != null) {
301 try {
302 // Wait for the InitializeTask to finish.
303 mInitializeTask.get();
304 } catch (CancellationException e1) {
305 Log.e(LOGTAG, "Native library load cancelled", e1);
306 } catch (ExecutionException e2) {
307 Log.e(LOGTAG, "Native library load failed", e2);
308 } catch (InterruptedException e3) {
309 Log.e(LOGTAG, "Native library load interrupted", e3);
310 }
311 }
312 completeInitializationOnUiThread(ctx);
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700313 mSynchronousInitialization = false;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700314 }
315
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700316 private static void initialize(Context ctx) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700317 if (!mInitializationCompleted) {
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700318 if (!mInitializationStarted) {
319 mInitializationStarted = true;
320 mUiThreadHandler = new Handler(Looper.getMainLooper());
321 Engine.initializeCommandLine(ctx);
322 mInitializeTask = new InitializeTask(ctx);
323 mInitializeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
324 mActivitySchedulerMap = new HashMap<BrowserActivity, ActivityScheduler>();
325 } else {
326 // This is not the first activity, wait for the engine initialization to finish.
327 initializeSync(ctx);
328 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700329 }
330 }
331
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700332 public static ActivityScheduler onActivityCreate(BrowserActivity activity) {
333 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
334
335 Context ctx = activity.getApplicationContext();
336 ActivityScheduler scheduler = new ActivityScheduler(activity);
337 initialize(ctx);
338
339 scheduler.onActivityCreate(mInitializationCompleted);
340 if (!mInitializationCompleted) {
341 mActivitySchedulerMap.put(activity, scheduler);
342 }
343 return scheduler;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700344 }
345
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700346 public static void onPostActivityCreate(BrowserActivity activity) {
347 EngineInitializer.initializeResourceExtractor(activity);
348 if (EngineInitializer.isInitialized()) {
349 activity.startController();
350 }
351 }
352
353 private static void completeInitializationOnUiThread(Context ctx) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700354 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
355
356 if (!mInitializationCompleted) {
357 // TODO: Evaluate the benefit of async Engine.initialize()
358 Engine.initialize(ctx);
Vivek Sekhardcf9d6b2014-12-01 15:08:37 -0800359 // Add the browser commandline options
360 BrowserConfig.getInstance(ctx).initCommandLineSwitches();
Tarun Nainania1d74d62015-01-07 12:40:09 -0800361
362 //Note: Only enable this for debugging.
Tarun Nainanif03c7962015-06-03 11:38:33 -0700363 if (BrowserCommandLine.hasSwitch(BrowserSwitches.STRICT_MODE)) {
Tarun Nainania1d74d62015-01-07 12:40:09 -0800364 Log.v(LOGTAG, "StrictMode enabled");
365 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
366 .detectDiskReads()
367 .detectDiskWrites()
368 .detectNetwork()
369 .penaltyLog()
370 .build());
371 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
372 .detectLeakedSqlLiteObjects()
373 .detectLeakedClosableObjects()
374 .penaltyLog()
375 .penaltyDeath()
376 .build());
377 }
378
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700379 //Enable remote debugging by default
380 Engine.setWebContentsDebuggingEnabled(true);
381 mInitializationCompleted = true;
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700382 mInitializationStarted = true;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700383 BrowserSettings.getInstance().onEngineInitializationComplete();
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700384 Engine.resumeTracing(ctx);
385
386 if (mActivitySchedulerMap != null) {
387 for (Map.Entry<BrowserActivity, ActivityScheduler> entry : mActivitySchedulerMap.entrySet()) {
388 entry.getValue().onEngineInitializationCompletion(mSynchronousInitialization);
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700389 }
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700390 mActivitySchedulerMap.clear();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700391 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700392 }
393 }
394
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700395 public static void initializeResourceExtractor(Context ctx) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700396 Engine.startExtractingResources(ctx);
397 }
398
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700399 public static void onPreDraw(BrowserActivity activity) {
400 activity.getScheduler().onPreDraw();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700401 }
402
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700403 public static void onActivityPause(BrowserActivity activity) {
404 activity.getScheduler().onActivityPause();
Tarun Nainanib4a173f2015-01-05 09:23:08 -0800405 }
406
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700407 public static void onActivityStop(BrowserActivity activity) {
408 activity.getScheduler().onActivityStop();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700409 }
410
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700411 public static void onActivityResume(BrowserActivity activity) {
412 activity.getScheduler().onActivityResume();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700413 }
414
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700415 public static void onActivityStart(BrowserActivity activity) {
416 activity.getScheduler().onActivityStart();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700417 }
418
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700419 public static void onActivityResult(BrowserActivity activity, int requestCode, int resultCode, Intent data) {
420 activity.getScheduler().onActivityResult(requestCode, resultCode, data);
421 }
422
423 public static void onNewIntent(BrowserActivity activity, Intent intent) {
Kevin Hart60a37f72015-02-09 17:14:14 -0800424 if (BrowserActivity.ACTION_RESTART.equals(intent.getAction())) {
425 Engine.releaseSpareChildProcess();
426 }
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700427 activity.getScheduler().onNewIntent(intent);
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700428 }
429
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700430 public static void onActivityDestroy(BrowserActivity activity) {
Kevin Hart4ad2edc2015-01-19 15:00:21 -0800431 Engine.releaseSpareChildProcess();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700432 }
Tarun Nainania1d74d62015-01-07 12:40:09 -0800433}