blob: a77c34af2906ad82c5b3c06d4d9583a03865fd00 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.androidx.webkit;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.SparseArray;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.webkit.SafeBrowsingResponseCompat;
import androidx.webkit.WebViewClientCompat;
import androidx.webkit.WebViewFeature;
/**
* An {@link Activity} which shows a custom interstitial if {@link WebView} encounters malicious
* resources. This class contains the logic for responding to user interaction with custom
* interstitials. The UI for these interstitials is implemented by {@link
* PopupInterstitialActivity}.
*/
public class CustomInterstitialActivity extends AppCompatActivity {
private WebView mWebView;
private CustomInterstitialWebViewClient mWebViewClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_interstitial);
setTitle(R.string.custom_interstitial_activity_title);
WebkitHelpers.appendWebViewVersionToTitle(this);
if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_HIT)
&& WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED)
&& WebViewFeature.isFeatureSupported(
WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) {
mWebView = findViewById(R.id.custom_interstitial_webview);
mWebViewClient = new CustomInterstitialWebViewClient(this);
mWebView.setWebViewClient(mWebViewClient);
mWebView.loadUrl(SafeBrowsingHelpers.TEST_SAFE_BROWSING_SITE);
} else {
WebkitHelpers.showMessageInActivity(this, R.string.webkit_api_not_available);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mWebViewClient.handleInterstitialResponse(requestCode, resultCode, data);
}
@Override
public void onBackPressed() {
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
super.onBackPressed();
}
}
private static class CustomInterstitialWebViewClient extends WebViewClientCompat {
private Activity mActivity;
private SparseArray<SafeBrowsingResponseCompat> mSafeBrowsingResponseMap;
int mActivityRequestCounter;
CustomInterstitialWebViewClient(Activity activity) {
mActivity = activity;
mSafeBrowsingResponseMap = new SparseArray<>();
mActivityRequestCounter = 0;
}
@Override
@RequiresApi(21) // This won't be called on < L, so we can safely apply @RequiresApi.
public void onSafeBrowsingHit(@NonNull WebView view,
@NonNull WebResourceRequest request, int threatType,
@NonNull SafeBrowsingResponseCompat callback) {
mSafeBrowsingResponseMap.put(mActivityRequestCounter, callback);
createInterstitial(threatType, request);
mActivityRequestCounter++;
}
@RequiresApi(21) // for WebResourceRequest
private void createInterstitial(int threatType, @NonNull WebResourceRequest request) {
Intent myIntent = new Intent(mActivity, PopupInterstitialActivity.class);
myIntent.putExtra(PopupInterstitialActivity.THREAT_TYPE, threatType);
myIntent.putExtra(PopupInterstitialActivity.THREAT_URL, request.getUrl().toString());
mActivity.startActivityForResult(myIntent, mActivityRequestCounter);
}
void handleInterstitialResponse(int requestCode, int resultCode, Intent data) {
// Get the correct SafeBrowsingResponse for the given interstitial Intent (there can be
// multiple Intents at the same time if multiple resources are malicious).
final SafeBrowsingResponseCompat response = mSafeBrowsingResponseMap.get(requestCode);
mSafeBrowsingResponseMap.delete(requestCode);
// Make sure the request was successful
if (resultCode == RESULT_OK) {
if (response == null) {
return;
}
// Figure out what navigation action we should take.
String result = data.getStringExtra(PopupInterstitialActivity.ACTION_RESPONSE);
// Figure out whether we should report this event.
boolean shouldSendReport = data.getBooleanExtra(
PopupInterstitialActivity.SHOULD_SEND_REPORT, false);
switch (result) {
case PopupInterstitialActivity.ACTION_RESPONSE_BACK_TO_SAFETY:
response.backToSafety(shouldSendReport);
break;
case PopupInterstitialActivity.ACTION_RESPONSE_PROCEED:
response.proceed(shouldSendReport);
break;
default:
break;
}
} else if (resultCode == RESULT_CANCELED) {
// User pressed the system's back button, treat this like backToSafety().
response.backToSafety(false);
} else {
throw new IllegalStateException("PopupInterstitialActivity shouldn't return any "
+ "nonstandard resultCodes");
}
}
}
}