1 /* 2 * Copyright 2019 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.example.androidx.webkit; 18 19 import android.app.Activity; 20 import android.content.Intent; 21 import android.os.Bundle; 22 import android.util.SparseArray; 23 import android.webkit.WebResourceRequest; 24 import android.webkit.WebView; 25 26 import androidx.appcompat.app.AppCompatActivity; 27 import androidx.webkit.SafeBrowsingResponseCompat; 28 import androidx.webkit.WebViewClientCompat; 29 import androidx.webkit.WebViewFeature; 30 31 import org.jspecify.annotations.NonNull; 32 import org.jspecify.annotations.Nullable; 33 34 /** 35 * An {@link Activity} which shows a custom interstitial if {@link WebView} encounters malicious 36 * resources. This class contains the logic for responding to user interaction with custom 37 * interstitials. The UI for these interstitials is implemented by {@link 38 * PopupInterstitialActivity}. 39 */ 40 public class CustomInterstitialActivity extends AppCompatActivity { 41 42 private WebView mWebView; 43 private CustomInterstitialWebViewClient mWebViewClient; 44 45 @Override onCreate(@ullable Bundle savedInstanceState)46 protected void onCreate(@Nullable Bundle savedInstanceState) { 47 super.onCreate(savedInstanceState); 48 setContentView(R.layout.activity_custom_interstitial); 49 setTitle(R.string.custom_interstitial_activity_title); 50 WebkitHelpers.appendWebViewVersionToTitle(this); 51 52 if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_HIT) 53 && WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED) 54 && WebViewFeature.isFeatureSupported( 55 WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) { 56 mWebView = findViewById(R.id.custom_interstitial_webview); 57 mWebViewClient = new CustomInterstitialWebViewClient(this); 58 mWebView.setWebViewClient(mWebViewClient); 59 mWebView.loadUrl(SafeBrowsingHelpers.TEST_SAFE_BROWSING_SITE); 60 } else { 61 WebkitHelpers.showMessageInActivity(this, R.string.webkit_api_not_available); 62 } 63 } 64 65 @Override onActivityResult(int requestCode, int resultCode, Intent data)66 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 67 super.onActivityResult(requestCode, resultCode, data); 68 mWebViewClient.handleInterstitialResponse(requestCode, resultCode, data); 69 } 70 71 @SuppressWarnings("deprecation") 72 @Override onBackPressed()73 public void onBackPressed() { 74 if (mWebView.canGoBack()) { 75 mWebView.goBack(); 76 } else { 77 super.onBackPressed(); 78 } 79 } 80 81 private static class CustomInterstitialWebViewClient extends WebViewClientCompat { 82 private final Activity mActivity; 83 private final SparseArray<SafeBrowsingResponseCompat> mSafeBrowsingResponseMap; 84 int mActivityRequestCounter; 85 CustomInterstitialWebViewClient(Activity activity)86 CustomInterstitialWebViewClient(Activity activity) { 87 mActivity = activity; 88 mSafeBrowsingResponseMap = new SparseArray<>(); 89 mActivityRequestCounter = 0; 90 } 91 92 @Override onSafeBrowsingHit(@onNull WebView view, @NonNull WebResourceRequest request, int threatType, @NonNull SafeBrowsingResponseCompat callback)93 public void onSafeBrowsingHit(@NonNull WebView view, @NonNull WebResourceRequest request, 94 int threatType, @NonNull SafeBrowsingResponseCompat callback) { 95 mSafeBrowsingResponseMap.put(mActivityRequestCounter, callback); 96 createInterstitial(threatType, request); 97 mActivityRequestCounter++; 98 } 99 createInterstitial(int threatType, @NonNull WebResourceRequest request)100 private void createInterstitial(int threatType, @NonNull WebResourceRequest request) { 101 Intent myIntent = new Intent(mActivity, PopupInterstitialActivity.class); 102 myIntent.putExtra(PopupInterstitialActivity.THREAT_TYPE, threatType); 103 myIntent.putExtra(PopupInterstitialActivity.THREAT_URL, 104 request.getUrl().toString()); 105 mActivity.startActivityForResult(myIntent, mActivityRequestCounter); 106 } 107 handleInterstitialResponse(int requestCode, int resultCode, Intent data)108 void handleInterstitialResponse(int requestCode, int resultCode, Intent data) { 109 // Get the correct SafeBrowsingResponse for the given interstitial Intent (there can be 110 // multiple Intents at the same time if multiple resources are malicious). 111 final SafeBrowsingResponseCompat response = mSafeBrowsingResponseMap.get(requestCode); 112 mSafeBrowsingResponseMap.delete(requestCode); 113 114 // Make sure the request was successful 115 if (resultCode == RESULT_OK) { 116 if (response == null) { 117 return; 118 } 119 120 // Figure out what navigation action we should take. 121 String result = data.getStringExtra(PopupInterstitialActivity.ACTION_RESPONSE); 122 // Figure out whether we should report this event. 123 boolean shouldSendReport = data.getBooleanExtra( 124 PopupInterstitialActivity.SHOULD_SEND_REPORT, false); 125 126 switch (result) { 127 case PopupInterstitialActivity.ACTION_RESPONSE_BACK_TO_SAFETY: 128 response.backToSafety(shouldSendReport); 129 break; 130 case PopupInterstitialActivity.ACTION_RESPONSE_PROCEED: 131 response.proceed(shouldSendReport); 132 break; 133 default: 134 break; 135 } 136 } else if (resultCode == RESULT_CANCELED) { 137 // User pressed the system's back button, treat this like backToSafety(). 138 response.backToSafety(false); 139 } else { 140 throw new IllegalStateException("PopupInterstitialActivity shouldn't return any " 141 + "nonstandard resultCodes"); 142 } 143 } 144 145 } 146 } 147