blob: 27ffbe0d8f4138d1a501edc5561646c9c0646f25 [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";
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070056 //Command line flag for strict mode
57 private final static String STRICT_MODE = "enable-strict-mode";
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070058
Kevin Harta3d001d2015-04-28 11:35:30 -070059 // Command line flag for single-process mode.
60 // Must match the value of kSingleProcess in content_switches.cc
61 private static final String SINGLE_PROCESS = "single-process";
62
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070063 private static boolean mInitializationStarted = false;
64 private static boolean mSynchronousInitialization = false;
65 private static boolean mInitializationCompleted = false;
66 private static Handler mUiThreadHandler;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070067
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070068 static class ActivityResult
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070069 {
70 public Intent data;
71 public int requestCode;
72 public int resultCode;
73
74 public ActivityResult(int requestCode, int resultCode, Intent data)
75 {
76 this.requestCode = requestCode;
77 this.resultCode = resultCode;
78 this.data = data;
79 }
80 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -070081
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -070082 public static class ActivityScheduler implements ViewTreeObserver.OnPreDrawListener
83 {
84 private BrowserActivity mActivity = null;
85 private ArrayList<ActivityResult> mPendingActivityResults = null;
86 private ArrayList<Intent> mPendingIntents = null;
87
88 private boolean mFirstDrawCompleted = false;
89 private boolean mOnStartPending = false;
90 private boolean mOnPausePending = false;
91 private boolean mEngineInitialized = false;
92 private boolean mCanForwardEvents = false;
93
94 public ActivityScheduler(BrowserActivity activity)
95 {
96 mActivity = activity;
97 mFirstDrawCompleted = false;
98 mOnStartPending = false;
99 mOnPausePending = false;
100 mPendingIntents = null;
101 mPendingActivityResults = null;
102 mEngineInitialized = false;
103 mCanForwardEvents = false;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700104 }
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700105
106 @VisibleForTesting
107 public boolean firstDrawCompleted() { return mFirstDrawCompleted; }
108 @VisibleForTesting
109 public boolean onStartPending() { return mOnStartPending; }
110 @VisibleForTesting
111 public boolean onPausePending() { return mOnPausePending; }
112 @VisibleForTesting
113 public boolean engineInitialized() { return mEngineInitialized; }
114 @VisibleForTesting
115 public boolean canForwardEvents() { return mCanForwardEvents; }
116
117 public void processPendingEvents() {
118 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
119
120 if (mOnStartPending) {
121 mOnStartPending = false;
122 mActivity.handleOnStart();
123 }
124 if (mOnPausePending) {
125 mActivity.handleOnPause();
126 mOnPausePending = false;
127 }
128 if (mPendingIntents != null) {
129 for (int i = 0; i < mPendingIntents.size(); i++) {
130 mActivity.handleOnNewIntent(mPendingIntents.get(i));
131 }
132 mPendingIntents = null;
133 }
134 if (mPendingActivityResults != null) {
135 for (int i = 0; i < mPendingActivityResults.size(); i++) {
136 ActivityResult result = mPendingActivityResults.get(i);
137 mActivity.handleOnActivityResult(result.requestCode, result.resultCode, result.data);
138 }
139 mPendingActivityResults = null;
140 }
141 mCanForwardEvents = true;
142 }
143
144 public void onActivityCreate(boolean engineInitialized) {
145 mEngineInitialized = engineInitialized;
146 if (!mEngineInitialized) {
147 // Engine initialization is not completed, we should wait for the onPreDraw() notification.
148 final ViewTreeObserver observer = mActivity.getWindow().getDecorView().getViewTreeObserver();
149 observer.addOnPreDrawListener(this);
150 } else {
151 mFirstDrawCompleted = true;
152 mCanForwardEvents = true;
153 }
154 }
155
156 @Override
157 public boolean onPreDraw() {
158 final ViewTreeObserver observer = mActivity.getWindow().getDecorView().getViewTreeObserver();
159 observer.removeOnPreDrawListener(this);
160
161 if (mFirstDrawCompleted)
162 return true;
163
164 mFirstDrawCompleted = true;
165 if (mEngineInitialized) {
166 postOnUiThread(new Runnable() {
167 @Override
168 public void run() {
169 mActivity.startController();
170 processPendingEvents();
171 }
172 });
173 }
174 return true;
175 }
176
177 public void onEngineInitializationCompletion(boolean synchronous) {
178 if (synchronous) {
179 // Don't wait for pre-draw notification if it is synchronous
180 onPreDraw();
181 }
182 mEngineInitialized = true;
183 if (mFirstDrawCompleted) {
184 mActivity.startController();
185 processPendingEvents();
186 }
187 }
188
189 public void onActivityPause() {
190 if (mCanForwardEvents) {
191 mActivity.handleOnPause();
192 return;
193 }
194 mOnPausePending = true;
195 }
196
197 public void onActivityResume() {
198 if (mCanForwardEvents) {
199 mActivity.handleOnResume();
200 return;
201 }
202 mOnPausePending = false;
203 }
204
205 public void onActivityStart() {
206 if (mCanForwardEvents) {
207 mActivity.handleOnStart();
208 // TODO: We have no reliable mechanism to know when the app goes background.
209 //ChildProcessLauncher.onBroughtToForeground();
210 return;
211 }
212 mOnStartPending = true;
213 }
214
215 public void onActivityStop() {
216 if (!mCanForwardEvents) {
217 initializeSync(mActivity.getApplicationContext());
218 }
219 mActivity.handleOnStop();
220 }
221
222 public void onActivityResult(int requestCode, int resultCode, Intent data) {
223 if (mCanForwardEvents) {
224 mActivity.handleOnActivityResult(requestCode, resultCode, data);
225 return;
226 }
227 if (mPendingActivityResults == null) {
228 mPendingActivityResults = new ArrayList<ActivityResult>(1);
229 }
230 mPendingActivityResults.add(new ActivityResult(requestCode, resultCode, data));
231 }
232
233 public void onNewIntent(Intent intent) {
234 if (mCanForwardEvents) {
235 mActivity.handleOnNewIntent(intent);
236 return;
237 }
238
239 if (mPendingIntents == null) {
240 mPendingIntents = new ArrayList<Intent>(1);
241 }
242 mPendingIntents.add(intent);
243 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700244 }
245
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700246 private static HashMap<BrowserActivity, ActivityScheduler> mActivitySchedulerMap = null;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700247 private static long sDelayForTesting = 0;
248
249 @VisibleForTesting
250 public static void setDelayForTesting(long delay)
251 {
252 sDelayForTesting = delay;
253 }
254
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700255 @VisibleForTesting
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700256 public static boolean isInitialized()
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700257 {
258 return mInitializationCompleted;
259 }
260
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700261 public static boolean runningOnUiThread() {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700262 return mUiThreadHandler.getLooper() == Looper.myLooper();
263 }
264
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700265 public static void postOnUiThread(Runnable task) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700266 mUiThreadHandler.post(task);
267 }
268
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700269 private static class InitializeTask extends AsyncTask<Void, Void, Boolean> {
270 private Context mApplicationContext;
271 public InitializeTask(Context ctx) {
272 mApplicationContext = ctx;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700273 }
274 @Override
275 protected Boolean doInBackground(Void... unused) {
276 try
277 {
278 // For testing.
279 if (sDelayForTesting > 0) {
280 Thread.sleep(sDelayForTesting);
281 }
282
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700283 Engine.loadNativeLibraries(mApplicationContext);
Kevin Harta3d001d2015-04-28 11:35:30 -0700284 if (!BrowserCommandLine.hasSwitch(SINGLE_PROCESS)) {
285 Engine.warmUpChildProcess(mApplicationContext);
286 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700287 return true;
288 }
289 catch (Exception e)
290 {
291 Log.e(LOGTAG, "Unable to load native library.", e);
292 }
293 return false;
294 }
295
296 @Override
297 protected void onPostExecute (Boolean result) {
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700298 completeInitializationOnUiThread(mApplicationContext);
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700299 }
300 }
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700301 private static InitializeTask mInitializeTask = null;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700302
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700303 public static void initializeSync(Context ctx) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700304 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700305 mSynchronousInitialization = true;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700306 if (mInitializeTask != null) {
307 try {
308 // Wait for the InitializeTask to finish.
309 mInitializeTask.get();
310 } catch (CancellationException e1) {
311 Log.e(LOGTAG, "Native library load cancelled", e1);
312 } catch (ExecutionException e2) {
313 Log.e(LOGTAG, "Native library load failed", e2);
314 } catch (InterruptedException e3) {
315 Log.e(LOGTAG, "Native library load interrupted", e3);
316 }
317 }
318 completeInitializationOnUiThread(ctx);
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700319 mSynchronousInitialization = false;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700320 }
321
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700322 private static void initialize(Context ctx) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700323 if (!mInitializationCompleted) {
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700324 if (!mInitializationStarted) {
325 mInitializationStarted = true;
326 mUiThreadHandler = new Handler(Looper.getMainLooper());
327 Engine.initializeCommandLine(ctx);
328 mInitializeTask = new InitializeTask(ctx);
329 mInitializeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
330 mActivitySchedulerMap = new HashMap<BrowserActivity, ActivityScheduler>();
331 } else {
332 // This is not the first activity, wait for the engine initialization to finish.
333 initializeSync(ctx);
334 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700335 }
336 }
337
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700338 public static ActivityScheduler onActivityCreate(BrowserActivity activity) {
339 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
340
341 Context ctx = activity.getApplicationContext();
342 ActivityScheduler scheduler = new ActivityScheduler(activity);
343 initialize(ctx);
344
345 scheduler.onActivityCreate(mInitializationCompleted);
346 if (!mInitializationCompleted) {
347 mActivitySchedulerMap.put(activity, scheduler);
348 }
349 return scheduler;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700350 }
351
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700352 public static void onPostActivityCreate(BrowserActivity activity) {
353 EngineInitializer.initializeResourceExtractor(activity);
354 if (EngineInitializer.isInitialized()) {
355 activity.startController();
356 }
357 }
358
359 private static void completeInitializationOnUiThread(Context ctx) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700360 assert runningOnUiThread() : "Tried to initialize the engine on the wrong thread.";
361
362 if (!mInitializationCompleted) {
363 // TODO: Evaluate the benefit of async Engine.initialize()
364 Engine.initialize(ctx);
Vivek Sekhardcf9d6b2014-12-01 15:08:37 -0800365 // Add the browser commandline options
366 BrowserConfig.getInstance(ctx).initCommandLineSwitches();
Tarun Nainania1d74d62015-01-07 12:40:09 -0800367
368 //Note: Only enable this for debugging.
369 if (BrowserCommandLine.hasSwitch(STRICT_MODE)) {
370 Log.v(LOGTAG, "StrictMode enabled");
371 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
372 .detectDiskReads()
373 .detectDiskWrites()
374 .detectNetwork()
375 .penaltyLog()
376 .build());
377 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
378 .detectLeakedSqlLiteObjects()
379 .detectLeakedClosableObjects()
380 .penaltyLog()
381 .penaltyDeath()
382 .build());
383 }
384
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700385 //Enable remote debugging by default
386 Engine.setWebContentsDebuggingEnabled(true);
387 mInitializationCompleted = true;
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700388 mInitializationStarted = true;
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700389 BrowserSettings.getInstance().onEngineInitializationComplete();
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700390 Engine.resumeTracing(ctx);
391
392 if (mActivitySchedulerMap != null) {
393 for (Map.Entry<BrowserActivity, ActivityScheduler> entry : mActivitySchedulerMap.entrySet()) {
394 entry.getValue().onEngineInitializationCompletion(mSynchronousInitialization);
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700395 }
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700396 mActivitySchedulerMap.clear();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700397 }
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700398 }
399 }
400
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700401 public static void initializeResourceExtractor(Context ctx) {
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700402 Engine.startExtractingResources(ctx);
403 }
404
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700405 public static void onPreDraw(BrowserActivity activity) {
406 activity.getScheduler().onPreDraw();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700407 }
408
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700409 public static void onActivityPause(BrowserActivity activity) {
410 activity.getScheduler().onActivityPause();
Tarun Nainanib4a173f2015-01-05 09:23:08 -0800411 }
412
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700413 public static void onActivityStop(BrowserActivity activity) {
414 activity.getScheduler().onActivityStop();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700415 }
416
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700417 public static void onActivityResume(BrowserActivity activity) {
418 activity.getScheduler().onActivityResume();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700419 }
420
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700421 public static void onActivityStart(BrowserActivity activity) {
422 activity.getScheduler().onActivityStart();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700423 }
424
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700425 public static void onActivityResult(BrowserActivity activity, int requestCode, int resultCode, Intent data) {
426 activity.getScheduler().onActivityResult(requestCode, resultCode, data);
427 }
428
429 public static void onNewIntent(BrowserActivity activity, Intent intent) {
Kevin Hart60a37f72015-02-09 17:14:14 -0800430 if (BrowserActivity.ACTION_RESTART.equals(intent.getAction())) {
431 Engine.releaseSpareChildProcess();
432 }
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700433 activity.getScheduler().onNewIntent(intent);
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700434 }
435
Kulanthaivel Palanichamy60aac812015-05-13 20:54:15 -0700436 public static void onActivityDestroy(BrowserActivity activity) {
Kevin Hart4ad2edc2015-01-19 15:00:21 -0800437 Engine.releaseSpareChildProcess();
Kulanthaivel Palanichamy77942682014-10-28 11:52:06 -0700438 }
Tarun Nainania1d74d62015-01-07 12:40:09 -0800439}