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