blob: 3e8269acce73ef688cf2f21f4fb2913d7a513f0e [file] [log] [blame]
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +01001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.webkit;
18
Gustav Sennton6258dcd2015-10-30 19:25:37 +000019import android.app.ActivityManagerNative;
20import android.app.AppGlobals;
Ben Murdochdc00a842014-07-17 14:55:00 +010021import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000025import android.content.pm.ApplicationInfo;
26import android.content.pm.PackageInfo;
27import android.content.pm.PackageManager;
28import android.content.pm.Signature;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010029import android.os.Binder;
30import android.os.Process;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000031import android.os.RemoteException;
Gustav Sennton23875b22016-02-09 14:11:33 +000032import android.os.UserHandle;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000033import android.provider.Settings;
34import android.provider.Settings.Secure;
35import android.util.AndroidRuntimeException;
Primiano Tucci810c0522014-07-25 18:03:16 +010036import android.util.Slog;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010037import android.webkit.IWebViewUpdateService;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000038import android.webkit.WebViewProviderInfo;
39import android.webkit.WebViewProviderResponse;
Ben Murdochdc00a842014-07-17 14:55:00 +010040import android.webkit.WebViewFactory;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010041
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +010042import com.android.server.SystemService;
43
Gustav Sennton6258dcd2015-10-30 19:25:37 +000044import java.util.ArrayList;
45import java.util.Arrays;
46import java.util.Iterator;
47import java.util.List;
48
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010049/**
50 * Private service to wait for the updatable WebView to be ready for use.
51 * @hide
52 */
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +010053public class WebViewUpdateService extends SystemService {
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010054
55 private static final String TAG = "WebViewUpdateService";
Gustav Sennton6258dcd2015-10-30 19:25:37 +000056 private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010057
Gustav Sennton6258dcd2015-10-30 19:25:37 +000058 // Keeps track of the number of running relro creations
59 private int mNumRelroCreationsStarted = 0;
60 private int mNumRelroCreationsFinished = 0;
61 // Implies that we need to rerun relro creation because we are using an out-of-date package
62 private boolean mWebViewPackageDirty = false;
63 // Set to true when the current provider is being replaced
64 private boolean mCurrentProviderBeingReplaced = false;
65 private boolean mAnyWebViewInstalled = false;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010066
Gustav Sennton6258dcd2015-10-30 19:25:37 +000067 private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
68
69 // The WebView package currently in use (or the one we are preparing).
70 private PackageInfo mCurrentWebViewPackage = null;
71 // The WebView providers that are currently available.
72 private WebViewProviderInfo[] mCurrentValidWebViewPackages = null;
Gustav Sennton6ce92c92015-10-23 11:10:39 +010073
Ben Murdochdc00a842014-07-17 14:55:00 +010074 private BroadcastReceiver mWebViewUpdatedReceiver;
75
76 public WebViewUpdateService(Context context) {
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +010077 super(context);
78 }
79
80 @Override
81 public void onStart() {
Ben Murdochdc00a842014-07-17 14:55:00 +010082 mWebViewUpdatedReceiver = new BroadcastReceiver() {
83 @Override
84 public void onReceive(Context context, Intent intent) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +000085 // When a package is replaced we will receive two intents, one representing
86 // the removal of the old package and one representing the addition of the
87 // new package.
88 // In the case where we receive an intent to remove the old version of the
89 // package that is being replaced we set a flag here and early-out so that we
90 // don't change provider while replacing the current package (we will instead
91 // change provider when the new version of the package is being installed).
Gustav Sennton3098cf22015-11-10 03:33:09 +000092 if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
Gustav Sennton6258dcd2015-10-30 19:25:37 +000093 && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
94 synchronized(this) {
95 if (mCurrentWebViewPackage == null) return;
96
97 String webViewPackage = "package:" + mCurrentWebViewPackage.packageName;
98 if (webViewPackage.equals(intent.getDataString()))
99 mCurrentProviderBeingReplaced = true;
100 }
101
Gustav Sennton3098cf22015-11-10 03:33:09 +0000102 return;
103 }
104
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000105 for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) {
106 String webviewPackage = "package:" + provider.packageName;
Gustav Sennton6ce92c92015-10-23 11:10:39 +0100107
108 if (webviewPackage.equals(intent.getDataString())) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000109 boolean updateWebView = false;
110 boolean removedOldPackage = false;
111 String oldProviderName = null;
112 PackageInfo newPackage = null;
113 synchronized(WebViewUpdateService.this) {
114 try {
115 updateValidWebViewPackages();
116 newPackage = findPreferredWebViewPackage();
117 if (mCurrentWebViewPackage != null)
118 oldProviderName = mCurrentWebViewPackage.packageName;
119 // Only trigger update actions if the updated package is the one
120 // that will be used, or the one that was in use before the
121 // update, or if we haven't seen a valid WebView package before.
122 updateWebView =
123 provider.packageName.equals(newPackage.packageName)
124 || provider.packageName.equals(oldProviderName)
125 || mCurrentWebViewPackage == null;
126 // We removed the old package if we received an intent to remove
127 // or replace the old package.
128 removedOldPackage =
129 provider.packageName.equals(oldProviderName);
130 if (updateWebView) {
131 onWebViewProviderChanged(newPackage);
132 }
133 } catch (WebViewFactory.MissingWebViewPackageException e) {
134 Slog.e(TAG, "Could not find valid WebView package to create " +
135 "relro with " + e);
136 }
137 }
138 if(updateWebView && !removedOldPackage && oldProviderName != null) {
139 // If the provider change is the result of adding or replacing a
140 // package that was not the previous provider then we must kill
141 // packages dependent on the old package ourselves. The framework
142 // only kills dependents of packages that are being removed.
143 try {
144 ActivityManagerNative.getDefault().killPackageDependents(
Gustav Sennton23875b22016-02-09 14:11:33 +0000145 oldProviderName, UserHandle.USER_ALL);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000146 } catch (RemoteException e) {
147 }
Gustav Sennton6ce92c92015-10-23 11:10:39 +0100148 }
149 return;
150 }
Ben Murdochdc00a842014-07-17 14:55:00 +0100151 }
152 }
153 };
154 IntentFilter filter = new IntentFilter();
Gustav Sennton3098cf22015-11-10 03:33:09 +0000155 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
156 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Ben Murdochdc00a842014-07-17 14:55:00 +0100157 filter.addDataScheme("package");
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100158 getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100159
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100160 publishBinderService("webviewupdate", new BinderService());
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100161 }
Ben Murdochdc00a842014-07-17 14:55:00 +0100162
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000163 /**
164 * Perform any WebView loading preparations that must happen at boot from the system server,
165 * after the package manager has started or after an update to the webview is installed.
166 * This must be called in the system server.
167 * Currently, this means spawning the child processes which will create the relro files.
168 */
169 public void prepareWebViewInSystemServer() {
170 try {
171 synchronized(this) {
172 updateValidWebViewPackages();
173 mCurrentWebViewPackage = findPreferredWebViewPackage();
174 onWebViewProviderChanged(mCurrentWebViewPackage);
175 }
176 } catch (Throwable t) {
177 // Log and discard errors at this stage as we must not crash the system server.
178 Slog.e(TAG, "error preparing webview provider from system server", t);
Ben Murdochdc00a842014-07-17 14:55:00 +0100179 }
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000180 }
181
182
183 /**
184 * Change WebView provider and provider setting and kill packages using the old provider.
185 */
186 private void changeProviderAndSetting(String newProviderName) {
187 PackageInfo oldPackage = null;
188 PackageInfo newPackage = null;
189 synchronized(this) {
190 oldPackage = mCurrentWebViewPackage;
191 updateUserSetting(newProviderName);
192
193 try {
194 newPackage = findPreferredWebViewPackage();
195 if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) {
196 // If we don't perform the user change, revert the settings change.
197 updateUserSetting(newPackage.packageName);
198 return;
199 }
200 } catch (WebViewFactory.MissingWebViewPackageException e) {
201 Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView package "
202 + e);
203 // If we don't perform the user change but don't have an installed WebView package,
204 // we will have changed the setting and it will be used when a package is available.
205 return;
206 }
207 onWebViewProviderChanged(newPackage);
208 }
209 // Kill apps using the old provider
210 try {
211 if (oldPackage != null) {
212 ActivityManagerNative.getDefault().killPackageDependents(
Gustav Sennton23875b22016-02-09 14:11:33 +0000213 oldPackage.packageName, UserHandle.USER_ALL);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000214 }
215 } catch (RemoteException e) {
216 }
217 return;
218 }
219
220 /**
221 * This is called when we change WebView provider, either when the current provider is updated
222 * or a new provider is chosen / takes precedence.
223 */
224 private void onWebViewProviderChanged(PackageInfo newPackage) {
225 synchronized(this) {
226 mAnyWebViewInstalled = true;
227 // If we have changed provider then the replacement of the old provider is
228 // irrelevant - we can only have chosen a new provider if its package is available.
229 mCurrentProviderBeingReplaced = false;
230 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
231 mCurrentWebViewPackage = newPackage;
232 updateUserSetting(newPackage.packageName);
233
234 // The relro creations might 'finish' (not start at all) before
235 // WebViewFactory.onWebViewProviderChanged which means we might not know the number
236 // of started creations before they finish.
237 mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
238 mNumRelroCreationsFinished = 0;
239 mNumRelroCreationsStarted = WebViewFactory.onWebViewProviderChanged(newPackage);
240 // If the relro creations finish before we know the number of started creations we
241 // will have to do any cleanup/notifying here.
242 checkIfRelrosDoneLocked();
243 } else {
244 mWebViewPackageDirty = true;
245 }
246 }
247 }
248
249 /**
250 * Updates the currently valid WebView provider packages.
251 * Should be used when a provider has been installed or removed.
252 * @hide
253 * */
254 private void updateValidWebViewPackages() {
255 List<WebViewProviderInfo> webViewProviders =
256 new ArrayList<WebViewProviderInfo>(Arrays.asList(WebViewFactory.getWebViewPackages()));
257 Iterator<WebViewProviderInfo> it = webViewProviders.iterator();
258 // remove non-valid packages
259 while(it.hasNext()) {
260 WebViewProviderInfo current = it.next();
261 if (!current.isValidProvider())
262 it.remove();
263 }
264 synchronized(this) {
265 mCurrentValidWebViewPackages =
266 webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
267 }
268 }
269
270 private static String getUserChosenWebViewProvider() {
271 return Settings.Secure.getString(AppGlobals.getInitialApplication().getContentResolver(),
272 Settings.Secure.WEBVIEW_PROVIDER);
273 }
274
275 private void updateUserSetting(String newProviderName) {
276 Settings.Secure.putString(getContext().getContentResolver(),
277 Settings.Secure.WEBVIEW_PROVIDER,
278 newProviderName == null ? "" : newProviderName);
279 }
280
281 /**
282 * Returns either the package info of the WebView provider determined in the following way:
283 * If the user has chosen a provider then use that if it is valid,
284 * otherwise use the first package in the webview priority list that is valid.
285 *
286 * @hide
287 */
288 private PackageInfo findPreferredWebViewPackage() {
289 WebViewProviderInfo[] providers = mCurrentValidWebViewPackages;
290
291 String userChosenProvider = getUserChosenWebViewProvider();
292
293 // If the user has chosen provider, use that
294 for (WebViewProviderInfo provider : providers) {
Gustav Sennton27f13de2016-01-05 20:22:02 +0000295 if (provider.packageName.equals(userChosenProvider) && provider.isEnabled()) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000296 return provider.getPackageInfo();
297 }
298 }
299
Gustav Sennton27f13de2016-01-05 20:22:02 +0000300 // User did not choose, or the choice failed; use the most stable provider that is
301 // enabled and available by default (not through user choice).
302 for (WebViewProviderInfo provider : providers) {
303 if (provider.isAvailableByDefault() && provider.isEnabled()) {
304 return provider.getPackageInfo();
305 }
306 }
307
308 // Could not find any enabled package either, use the most stable provider.
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000309 for (WebViewProviderInfo provider : providers) {
310 return provider.getPackageInfo();
311 }
Gustav Sennton27f13de2016-01-05 20:22:02 +0000312
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000313 mAnyWebViewInstalled = false;
314 throw new WebViewFactory.MissingWebViewPackageException(
315 "Could not find a loadable WebView package");
316 }
317
318 /**
319 * Returns whether WebView is ready and is not going to go through its preparation phase again
320 * directly.
321 */
322 private boolean webViewIsReadyLocked() {
323 return !mWebViewPackageDirty
324 && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
325 && !mCurrentProviderBeingReplaced
326 // The current package might be replaced though we haven't received an intent declaring
327 // this yet, the following flag makes anyone loading WebView to wait in this case.
328 && mAnyWebViewInstalled;
329 }
330
331 private void checkIfRelrosDoneLocked() {
332 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
333 if (mWebViewPackageDirty) {
334 mWebViewPackageDirty = false;
335 // If we have changed provider since we started the relro creation we need to
336 // redo the whole process using the new package instead.
337 // Though, if the current provider package is being replaced we don't want to change
338 // provider here since we will perform the change either when the package is added
339 // again or when we switch to another provider (whichever comes first).
340 if (!mCurrentProviderBeingReplaced) {
341 PackageInfo newPackage = findPreferredWebViewPackage();
342 onWebViewProviderChanged(newPackage);
343 }
344 } else {
345 this.notifyAll();
346 }
347 }
Ben Murdochdc00a842014-07-17 14:55:00 +0100348 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100349
350 private class BinderService extends IWebViewUpdateService.Stub {
351
352 /**
353 * The shared relro process calls this to notify us that it's done trying to create a relro
354 * file. This method gets called even if the relro creation has failed or the process
355 * crashed.
356 */
357 @Override // Binder call
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000358 public void notifyRelroCreationCompleted() {
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100359 // Verify that the caller is either the shared relro process (nominal case) or the
360 // system server (only in the case the relro process crashes and we get here via the
361 // crashHandler).
362 if (Binder.getCallingUid() != Process.SHARED_RELRO_UID &&
363 Binder.getCallingUid() != Process.SYSTEM_UID) {
364 return;
365 }
366
367 synchronized (WebViewUpdateService.this) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000368 mNumRelroCreationsFinished++;
369 checkIfRelrosDoneLocked();
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100370 }
371 }
372
373 /**
374 * WebViewFactory calls this to block WebView loading until the relro file is created.
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000375 * Returns the WebView provider for which we create relro files.
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100376 */
377 @Override // Binder call
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000378 public WebViewProviderResponse waitForAndGetProvider() {
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100379 // The WebViewUpdateService depends on the prepareWebViewInSystemServer call, which
380 // happens later (during the PHASE_ACTIVITY_MANAGER_READY) in SystemServer.java. If
381 // another service there tries to bring up a WebView in the between, the wait below
382 // would deadlock without the check below.
383 if (Binder.getCallingPid() == Process.myPid()) {
384 throw new IllegalStateException("Cannot create a WebView from the SystemServer");
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100385 }
386
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000387 PackageInfo webViewPackage = null;
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100388 final long NS_PER_MS = 1000000;
389 final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000390 boolean webViewReady = false;
391 int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100392 synchronized (WebViewUpdateService.this) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000393 webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
394 while (!webViewReady) {
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100395 final long timeNowMs = System.nanoTime() / NS_PER_MS;
396 if (timeNowMs >= timeoutTimeMs) break;
397 try {
398 WebViewUpdateService.this.wait(timeoutTimeMs - timeNowMs);
399 } catch (InterruptedException e) {}
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000400 webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
401 }
402 // Make sure we return the provider that was used to create the relro file
403 webViewPackage = WebViewUpdateService.this.mCurrentWebViewPackage;
404 if (webViewReady) {
405 } else if (mCurrentProviderBeingReplaced) {
406 // It is important that we check this flag before the one representing WebView
407 // being installed, otherwise we might think there is no WebView though the
408 // current one is just being replaced.
409 webViewStatus = WebViewFactory.LIBLOAD_WEBVIEW_BEING_REPLACED;
410 } else if (!mAnyWebViewInstalled) {
411 webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
412 } else {
413 // Either the current relro creation isn't done yet, or the new relro creatioin
414 // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
415 webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100416 }
417 }
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000418 if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
419 return new WebViewProviderResponse(webViewPackage, webViewStatus);
420 }
421
422 /**
423 * This is called from DeveloperSettings when the user changes WebView provider.
424 */
425 @Override // Binder call
426 public void changeProviderAndSetting(String newProvider) {
427 if (getContext().checkCallingPermission(
428 android.Manifest.permission.WRITE_SECURE_SETTINGS)
429 != PackageManager.PERMISSION_GRANTED) {
430 String msg = "Permission Denial: changeProviderAndSetting() from pid="
431 + Binder.getCallingPid()
432 + ", uid=" + Binder.getCallingUid()
433 + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
434 Slog.w(TAG, msg);
435 throw new SecurityException(msg);
436 }
437
438 WebViewUpdateService.this.changeProviderAndSetting(newProvider);
439 }
440
441 @Override // Binder call
442 public WebViewProviderInfo[] getValidWebViewPackages() {
443 synchronized(WebViewUpdateService.this) {
444 return mCurrentValidWebViewPackages;
445 }
446 }
447
448 @Override // Binder call
449 public String getCurrentWebViewPackageName() {
450 synchronized(WebViewUpdateService.this) {
451 if (WebViewUpdateService.this.mCurrentWebViewPackage == null)
452 return null;
453 return WebViewUpdateService.this.mCurrentWebViewPackage.packageName;
454 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100455 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100456 }
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100457}