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 17package com.android.server.webkit; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.content.pm.PackageInfo; 24import android.content.pm.PackageManager; 25import android.os.Binder; 26import android.os.PatternMatcher; 27import android.os.Process; 28import android.os.ResultReceiver; 29import android.os.ShellCallback; 30import android.os.UserHandle; 31import android.util.Slog; 32import android.webkit.IWebViewUpdateService; 33import android.webkit.WebViewProviderInfo; 34import android.webkit.WebViewProviderResponse; 35 36import com.android.internal.util.DumpUtils; 37import com.android.server.SystemService; 38 39import java.io.FileDescriptor; 40import java.io.PrintWriter; 41import java.util.Arrays; 42 43/** 44 * Private service to wait for the updatable WebView to be ready for use. 45 * @hide 46 */ 47public class WebViewUpdateService extends SystemService { 48 49 private static final String TAG = "WebViewUpdateService"; 50 51 private BroadcastReceiver mWebViewUpdatedReceiver; 52 private WebViewUpdateServiceImpl mImpl; 53 54 static final int PACKAGE_CHANGED = 0; 55 static final int PACKAGE_ADDED = 1; 56 static final int PACKAGE_ADDED_REPLACED = 2; 57 static final int PACKAGE_REMOVED = 3; 58 59 public WebViewUpdateService(Context context) { 60 super(context); 61 mImpl = new WebViewUpdateServiceImpl(context, SystemImpl.getInstance()); 62 } 63 64 @Override 65 public void onStart() { 66 mWebViewUpdatedReceiver = new BroadcastReceiver() { 67 @Override 68 public void onReceive(Context context, Intent intent) { 69 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 70 switch (intent.getAction()) { 71 case Intent.ACTION_PACKAGE_REMOVED: 72 // When a package is replaced we will receive two intents, one 73 // representing the removal of the old package and one representing the 74 // addition of the new package. 75 // In the case where we receive an intent to remove the old version of 76 // the package that is being replaced we early-out here so that we don't 77 // run the update-logic twice. 78 if (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) return; 79 mImpl.packageStateChanged(packageNameFromIntent(intent), 80 PACKAGE_REMOVED, userId); 81 break; 82 case Intent.ACTION_PACKAGE_CHANGED: 83 // Ensure that we only heed PACKAGE_CHANGED intents if they change an 84 // entire package, not just a component 85 if (entirePackageChanged(intent)) { 86 mImpl.packageStateChanged(packageNameFromIntent(intent), 87 PACKAGE_CHANGED, userId); 88 } 89 break; 90 case Intent.ACTION_PACKAGE_ADDED: 91 mImpl.packageStateChanged(packageNameFromIntent(intent), 92 (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING) 93 ? PACKAGE_ADDED_REPLACED : PACKAGE_ADDED), userId); 94 break; 95 case Intent.ACTION_USER_STARTED: 96 mImpl.handleNewUser(userId); 97 break; 98 case Intent.ACTION_USER_REMOVED: 99 mImpl.handleUserRemoved(userId); 100 break; 101 } 102 } 103 }; 104 IntentFilter filter = new IntentFilter(); 105 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 106 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 107 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 108 filter.addDataScheme("package"); 109 // Make sure we only receive intents for WebView packages from our config file. 110 for (WebViewProviderInfo provider : mImpl.getWebViewPackages()) { 111 filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL); 112 } 113 114 getContext().registerReceiverAsUser(mWebViewUpdatedReceiver, UserHandle.ALL, filter, 115 null /* broadcast permission */, null /* handler */); 116 117 IntentFilter userAddedFilter = new IntentFilter(); 118 userAddedFilter.addAction(Intent.ACTION_USER_STARTED); 119 userAddedFilter.addAction(Intent.ACTION_USER_REMOVED); 120 getContext().registerReceiverAsUser(mWebViewUpdatedReceiver, UserHandle.ALL, 121 userAddedFilter, null /* broadcast permission */, null /* handler */); 122 123 publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/); 124 } 125 126 public void prepareWebViewInSystemServer() { 127 mImpl.prepareWebViewInSystemServer(); 128 } 129 130 private static String packageNameFromIntent(Intent intent) { 131 return intent.getDataString().substring("package:".length()); 132 } 133 134 /** 135 * Returns whether the entire package from an ACTION_PACKAGE_CHANGED intent was changed (rather 136 * than just one of its components). 137 * @hide 138 */ 139 public static boolean entirePackageChanged(Intent intent) { 140 String[] componentList = 141 intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); 142 return Arrays.asList(componentList).contains( 143 intent.getDataString().substring("package:".length())); 144 } 145 146 private class BinderService extends IWebViewUpdateService.Stub { 147 148 @Override 149 public void onShellCommand(FileDescriptor in, FileDescriptor out, 150 FileDescriptor err, String[] args, ShellCallback callback, 151 ResultReceiver resultReceiver) { 152 (new WebViewUpdateServiceShellCommand(this)).exec( 153 this, in, out, err, args, callback, resultReceiver); 154 } 155 156 157 /** 158 * The shared relro process calls this to notify us that it's done trying to create a relro 159 * file. This method gets called even if the relro creation has failed or the process 160 * crashed. 161 */ 162 @Override // Binder call 163 public void notifyRelroCreationCompleted() { 164 // Verify that the caller is either the shared relro process (nominal case) or the 165 // system server (only in the case the relro process crashes and we get here via the 166 // crashHandler). 167 if (Binder.getCallingUid() != Process.SHARED_RELRO_UID && 168 Binder.getCallingUid() != Process.SYSTEM_UID) { 169 return; 170 } 171 172 long callingId = Binder.clearCallingIdentity(); 173 try { 174 WebViewUpdateService.this.mImpl.notifyRelroCreationCompleted(); 175 } finally { 176 Binder.restoreCallingIdentity(callingId); 177 } 178 } 179 180 /** 181 * WebViewFactory calls this to block WebView loading until the relro file is created. 182 * Returns the WebView provider for which we create relro files. 183 */ 184 @Override // Binder call 185 public WebViewProviderResponse waitForAndGetProvider() { 186 // The WebViewUpdateService depends on the prepareWebViewInSystemServer call, which 187 // happens later (during the PHASE_ACTIVITY_MANAGER_READY) in SystemServer.java. If 188 // another service there tries to bring up a WebView in the between, the wait below 189 // would deadlock without the check below. 190 if (Binder.getCallingPid() == Process.myPid()) { 191 throw new IllegalStateException("Cannot create a WebView from the SystemServer"); 192 } 193 194 return WebViewUpdateService.this.mImpl.waitForAndGetProvider(); 195 } 196 197 /** 198 * This is called from DeveloperSettings when the user changes WebView provider. 199 */ 200 @Override // Binder call 201 public String changeProviderAndSetting(String newProvider) { 202 if (getContext().checkCallingPermission( 203 android.Manifest.permission.WRITE_SECURE_SETTINGS) 204 != PackageManager.PERMISSION_GRANTED) { 205 String msg = "Permission Denial: changeProviderAndSetting() from pid=" 206 + Binder.getCallingPid() 207 + ", uid=" + Binder.getCallingUid() 208 + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS; 209 Slog.w(TAG, msg); 210 throw new SecurityException(msg); 211 } 212 213 long callingId = Binder.clearCallingIdentity(); 214 try { 215 return WebViewUpdateService.this.mImpl.changeProviderAndSetting( 216 newProvider); 217 } finally { 218 Binder.restoreCallingIdentity(callingId); 219 } 220 } 221 222 @Override // Binder call 223 public WebViewProviderInfo[] getValidWebViewPackages() { 224 return WebViewUpdateService.this.mImpl.getValidWebViewPackages(); 225 } 226 227 @Override // Binder call 228 public WebViewProviderInfo[] getAllWebViewPackages() { 229 return WebViewUpdateService.this.mImpl.getWebViewPackages(); 230 } 231 232 @Override // Binder call 233 public String getCurrentWebViewPackageName() { 234 PackageInfo pi = WebViewUpdateService.this.mImpl.getCurrentWebViewPackage(); 235 return pi == null ? null : pi.packageName; 236 } 237 238 @Override // Binder call 239 public PackageInfo getCurrentWebViewPackage() { 240 return WebViewUpdateService.this.mImpl.getCurrentWebViewPackage(); 241 } 242 243 @Override // Binder call 244 public boolean isFallbackPackage(String packageName) { 245 return WebViewUpdateService.this.mImpl.isFallbackPackage(packageName); 246 } 247 248 @Override // Binder call 249 public void enableFallbackLogic(boolean enable) { 250 if (getContext().checkCallingPermission( 251 android.Manifest.permission.WRITE_SECURE_SETTINGS) 252 != PackageManager.PERMISSION_GRANTED) { 253 String msg = "Permission Denial: enableFallbackLogic() from pid=" 254 + Binder.getCallingPid() 255 + ", uid=" + Binder.getCallingUid() 256 + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS; 257 Slog.w(TAG, msg); 258 throw new SecurityException(msg); 259 } 260 261 long callingId = Binder.clearCallingIdentity(); 262 try { 263 WebViewUpdateService.this.mImpl.enableFallbackLogic(enable); 264 } finally { 265 Binder.restoreCallingIdentity(callingId); 266 } 267 } 268 269 @Override // Binder call 270 public boolean isMultiProcessEnabled() { 271 return WebViewUpdateService.this.mImpl.isMultiProcessEnabled(); 272 } 273 274 @Override // Binder call 275 public void enableMultiProcess(boolean enable) { 276 if (getContext().checkCallingPermission( 277 android.Manifest.permission.WRITE_SECURE_SETTINGS) 278 != PackageManager.PERMISSION_GRANTED) { 279 String msg = "Permission Denial: enableMultiProcess() from pid=" 280 + Binder.getCallingPid() 281 + ", uid=" + Binder.getCallingUid() 282 + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS; 283 Slog.w(TAG, msg); 284 throw new SecurityException(msg); 285 } 286 287 long callingId = Binder.clearCallingIdentity(); 288 try { 289 WebViewUpdateService.this.mImpl.enableMultiProcess(enable); 290 } finally { 291 Binder.restoreCallingIdentity(callingId); 292 } 293 } 294 295 @Override 296 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 297 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; 298 WebViewUpdateService.this.mImpl.dumpState(pw); 299 } 300 } 301} 302