Home | History | Annotate | Download | only in webkit
      1 /*
      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 
     17 package com.android.server.webkit;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.os.Binder;
     24 import android.os.Process;
     25 import android.util.Slog;
     26 import android.webkit.IWebViewUpdateService;
     27 import android.webkit.WebViewFactory;
     28 
     29 import com.android.server.SystemService;
     30 
     31 /**
     32  * Private service to wait for the updatable WebView to be ready for use.
     33  * @hide
     34  */
     35 public class WebViewUpdateService extends SystemService {
     36 
     37     private static final String TAG = "WebViewUpdateService";
     38     private static final int WAIT_TIMEOUT_MS = 5000; // Same as KEY_DISPATCHING_TIMEOUT.
     39 
     40     private boolean mRelroReady32Bit = false;
     41     private boolean mRelroReady64Bit = false;
     42 
     43     private BroadcastReceiver mWebViewUpdatedReceiver;
     44 
     45     public WebViewUpdateService(Context context) {
     46         super(context);
     47     }
     48 
     49     @Override
     50     public void onStart() {
     51         mWebViewUpdatedReceiver = new BroadcastReceiver() {
     52                 @Override
     53                 public void onReceive(Context context, Intent intent) {
     54                     String webviewPackage = "package:" + WebViewFactory.getWebViewPackageName();
     55                     if (webviewPackage.equals(intent.getDataString())) {
     56                         onWebViewUpdateInstalled();
     57                     }
     58                 }
     59         };
     60         IntentFilter filter = new IntentFilter();
     61         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
     62         filter.addDataScheme("package");
     63         getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
     64 
     65         publishBinderService("webviewupdate", new BinderService());
     66     }
     67 
     68     private void onWebViewUpdateInstalled() {
     69         Slog.d(TAG, "WebView Package updated!");
     70 
     71         synchronized (this) {
     72             mRelroReady32Bit = false;
     73             mRelroReady64Bit = false;
     74         }
     75         WebViewFactory.onWebViewUpdateInstalled();
     76     }
     77 
     78     private class BinderService extends IWebViewUpdateService.Stub {
     79 
     80         /**
     81          * The shared relro process calls this to notify us that it's done trying to create a relro
     82          * file. This method gets called even if the relro creation has failed or the process
     83          * crashed.
     84          */
     85         @Override // Binder call
     86         public void notifyRelroCreationCompleted(boolean is64Bit, boolean success) {
     87             // Verify that the caller is either the shared relro process (nominal case) or the
     88             // system server (only in the case the relro process crashes and we get here via the
     89             // crashHandler).
     90             if (Binder.getCallingUid() != Process.SHARED_RELRO_UID &&
     91                     Binder.getCallingUid() != Process.SYSTEM_UID) {
     92                 return;
     93             }
     94 
     95             synchronized (WebViewUpdateService.this) {
     96                 if (is64Bit) {
     97                     mRelroReady64Bit = true;
     98                 } else {
     99                     mRelroReady32Bit = true;
    100                 }
    101                 WebViewUpdateService.this.notifyAll();
    102             }
    103         }
    104 
    105         /**
    106          * WebViewFactory calls this to block WebView loading until the relro file is created.
    107          */
    108         @Override // Binder call
    109         public void waitForRelroCreationCompleted(boolean is64Bit) {
    110             // The WebViewUpdateService depends on the prepareWebViewInSystemServer call, which
    111             // happens later (during the PHASE_ACTIVITY_MANAGER_READY) in SystemServer.java. If
    112             // another service there tries to bring up a WebView in the between, the wait below
    113             // would deadlock without the check below.
    114             if (Binder.getCallingPid() == Process.myPid()) {
    115                 throw new IllegalStateException("Cannot create a WebView from the SystemServer");
    116             }
    117 
    118             final long NS_PER_MS = 1000000;
    119             final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
    120             boolean relroReady = (is64Bit ? mRelroReady64Bit : mRelroReady32Bit);
    121             synchronized (WebViewUpdateService.this) {
    122                 while (!relroReady) {
    123                     final long timeNowMs = System.nanoTime() / NS_PER_MS;
    124                     if (timeNowMs >= timeoutTimeMs) break;
    125                     try {
    126                         WebViewUpdateService.this.wait(timeoutTimeMs - timeNowMs);
    127                     } catch (InterruptedException e) {}
    128                     relroReady = (is64Bit ? mRelroReady64Bit : mRelroReady32Bit);
    129                 }
    130             }
    131             if (!relroReady) Slog.w(TAG, "creating relro file timed out");
    132         }
    133     }
    134 
    135 }
    136