1 /* 2 * Copyright 2018 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 androidx.webkit.internal; 18 19 import android.annotation.SuppressLint; 20 import android.webkit.ServiceWorkerWebSettings; 21 22 import androidx.annotation.NonNull; 23 import androidx.annotation.RequiresApi; 24 import androidx.webkit.ServiceWorkerWebSettingsCompat; 25 26 import org.chromium.support_lib_boundary.ServiceWorkerWebSettingsBoundaryInterface; 27 import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil; 28 29 import java.lang.reflect.InvocationHandler; 30 import java.lang.reflect.Proxy; 31 32 /** 33 * Implementation of {@link ServiceWorkerWebSettingsCompat}. 34 * This class uses either the framework, the WebView APK, or both, to implement 35 * {@link ServiceWorkerWebSettingsCompat} functionality. 36 */ 37 public class ServiceWorkerWebSettingsImpl extends ServiceWorkerWebSettingsCompat { 38 private ServiceWorkerWebSettings mFrameworksImpl; 39 private ServiceWorkerWebSettingsBoundaryInterface mBoundaryInterface; 40 41 /** 42 * This class handles three different scenarios: 43 * 1. The Android version on the device is high enough to support all APIs used. 44 * 2. The Android version on the device is too low to support any ServiceWorkerWebSettings APIs 45 * so we use the support library glue instead through 46 * {@link ServiceWorkerWebSettingsBoundaryInterface}. 47 * 3. The Android version on the device is high enough to support some ServiceWorkerWebSettings 48 * APIs, so we call into them using {@link android.webkit.ServiceWorkerWebSettings}, but the 49 * rest of the APIs are only supported by the support library glue, so whenever we call such an 50 * API we fetch a {@link ServiceWorkerWebSettingsBoundaryInterface} corresponding to our 51 * {@link android.webkit.ServiceWorkerWebSettings}. 52 */ 53 public ServiceWorkerWebSettingsImpl(@NonNull ServiceWorkerWebSettings settings) { 54 mFrameworksImpl = settings; 55 } 56 57 public ServiceWorkerWebSettingsImpl(@NonNull InvocationHandler invocationHandler) { 58 mBoundaryInterface = BoundaryInterfaceReflectionUtil.castToSuppLibClass( 59 ServiceWorkerWebSettingsBoundaryInterface.class, invocationHandler); 60 } 61 62 @RequiresApi(24) 63 private ServiceWorkerWebSettings getFrameworksImpl() { 64 if (mFrameworksImpl == null) { 65 mFrameworksImpl = 66 WebViewGlueCommunicator.getCompatConverter().convertServiceWorkerSettings( 67 Proxy.getInvocationHandler(mBoundaryInterface)); 68 } 69 return mFrameworksImpl; 70 } 71 72 private ServiceWorkerWebSettingsBoundaryInterface getBoundaryInterface() { 73 if (mBoundaryInterface == null) { 74 // If the boundary interface is null we must have a working frameworks implementation to 75 // convert into a boundary interface. 76 // The case of the boundary interface being null here only occurs if we created the 77 // ServiceWorkerWebSettingsImpl using a frameworks API, but now want to call an API on 78 // the ServiceWorkerWebSettingsImpl that is only supported by the support library glue. 79 // This could happen for example if we introduce a new ServiceWorkerWebSettings API in 80 // level 30 and we run the support library on an N device (whose framework supports 81 // ServiceWorkerWebSettings). 82 mBoundaryInterface = BoundaryInterfaceReflectionUtil.castToSuppLibClass( 83 ServiceWorkerWebSettingsBoundaryInterface.class, 84 WebViewGlueCommunicator.getCompatConverter().convertServiceWorkerSettings( 85 mFrameworksImpl)); 86 } 87 return mBoundaryInterface; 88 } 89 90 @SuppressLint("NewApi") 91 @Override 92 public void setCacheMode(int mode) { 93 final WebViewFeatureInternal feature = WebViewFeatureInternal.SERVICE_WORKER_CACHE_MODE; 94 if (feature.isSupportedByFramework()) { 95 getFrameworksImpl().setCacheMode(mode); 96 } else if (feature.isSupportedByWebView()) { 97 getBoundaryInterface().setCacheMode(mode); 98 } else { 99 throw WebViewFeatureInternal.getUnsupportedOperationException(); 100 } 101 } 102 103 @SuppressLint("NewApi") 104 @Override 105 public int getCacheMode() { 106 final WebViewFeatureInternal feature = WebViewFeatureInternal.SERVICE_WORKER_CACHE_MODE; 107 if (feature.isSupportedByFramework()) { 108 return getFrameworksImpl().getCacheMode(); 109 } else if (feature.isSupportedByWebView()) { 110 return getBoundaryInterface().getCacheMode(); 111 } else { 112 throw WebViewFeatureInternal.getUnsupportedOperationException(); 113 } 114 } 115 116 @SuppressLint("NewApi") 117 @Override 118 public void setAllowContentAccess(boolean allow) { 119 final WebViewFeatureInternal feature = WebViewFeatureInternal.SERVICE_WORKER_CONTENT_ACCESS; 120 if (feature.isSupportedByFramework()) { 121 getFrameworksImpl().setAllowContentAccess(allow); 122 } else if (feature.isSupportedByWebView()) { 123 getBoundaryInterface().setAllowContentAccess(allow); 124 } else { 125 throw WebViewFeatureInternal.getUnsupportedOperationException(); 126 } 127 } 128 129 @SuppressLint("NewApi") 130 @Override 131 public boolean getAllowContentAccess() { 132 final WebViewFeatureInternal feature = WebViewFeatureInternal.SERVICE_WORKER_CONTENT_ACCESS; 133 if (feature.isSupportedByFramework()) { 134 return getFrameworksImpl().getAllowContentAccess(); 135 } else if (feature.isSupportedByWebView()) { 136 return getBoundaryInterface().getAllowContentAccess(); 137 } else { 138 throw WebViewFeatureInternal.getUnsupportedOperationException(); 139 } 140 } 141 142 @SuppressLint("NewApi") 143 @Override 144 public void setAllowFileAccess(boolean allow) { 145 final WebViewFeatureInternal feature = WebViewFeatureInternal.SERVICE_WORKER_FILE_ACCESS; 146 if (feature.isSupportedByFramework()) { 147 getFrameworksImpl().setAllowFileAccess(allow); 148 } else if (feature.isSupportedByWebView()) { 149 getBoundaryInterface().setAllowFileAccess(allow); 150 } else { 151 throw WebViewFeatureInternal.getUnsupportedOperationException(); 152 } 153 } 154 155 @SuppressLint("NewApi") 156 @Override 157 public boolean getAllowFileAccess() { 158 final WebViewFeatureInternal feature = WebViewFeatureInternal.SERVICE_WORKER_FILE_ACCESS; 159 if (feature.isSupportedByFramework()) { 160 return getFrameworksImpl().getAllowFileAccess(); 161 } else if (feature.isSupportedByWebView()) { 162 return getBoundaryInterface().getAllowFileAccess(); 163 } else { 164 throw WebViewFeatureInternal.getUnsupportedOperationException(); 165 } 166 } 167 168 @SuppressLint("NewApi") 169 @Override 170 public void setBlockNetworkLoads(boolean flag) { 171 final WebViewFeatureInternal feature = 172 WebViewFeatureInternal.SERVICE_WORKER_BLOCK_NETWORK_LOADS; 173 if (feature.isSupportedByFramework()) { 174 getFrameworksImpl().setBlockNetworkLoads(flag); 175 } else if (feature.isSupportedByWebView()) { 176 getBoundaryInterface().setBlockNetworkLoads(flag); 177 } else { 178 throw WebViewFeatureInternal.getUnsupportedOperationException(); 179 } 180 } 181 182 @SuppressLint("NewApi") 183 @Override 184 public boolean getBlockNetworkLoads() { 185 final WebViewFeatureInternal feature = 186 WebViewFeatureInternal.SERVICE_WORKER_BLOCK_NETWORK_LOADS; 187 if (feature.isSupportedByFramework()) { 188 return getFrameworksImpl().getBlockNetworkLoads(); 189 } else if (feature.isSupportedByWebView()) { 190 return getBoundaryInterface().getBlockNetworkLoads(); 191 } else { 192 throw WebViewFeatureInternal.getUnsupportedOperationException(); 193 } 194 } 195 } 196