OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/android/safe_browsing/safe_browsing_api_handler_bridge.
h" | |
6 | |
7 #include <memory> | |
8 #include <string> | |
9 | |
10 #include "base/android/context_utils.h" | |
11 #include "base/android/jni_android.h" | |
12 #include "base/android/jni_array.h" | |
13 #include "base/android/jni_string.h" | |
14 #include "base/metrics/histogram_macros.h" | |
15 #include "chrome/browser/safe_browsing/safe_browsing_util.h" | |
16 #include "components/safe_browsing_db/safe_browsing_api_handler_util.h" | |
17 #include "content/public/browser/browser_thread.h" | |
18 #include "jni/SafeBrowsingApiBridge_jni.h" | |
19 | |
20 using base::android::AttachCurrentThread; | |
21 using base::android::ConvertJavaStringToUTF8; | |
22 using base::android::ConvertUTF8ToJavaString; | |
23 using base::android::GetApplicationContext; | |
24 using base::android::JavaParamRef; | |
25 using base::android::ScopedJavaLocalRef; | |
26 using base::android::ToJavaIntArray; | |
27 using content::BrowserThread; | |
28 | |
29 namespace safe_browsing { | |
30 | |
31 namespace { | |
32 // Takes ownership of callback ptr. | |
33 void RunCallbackOnIOThread( | |
34 SafeBrowsingApiHandler::URLCheckCallbackMeta* callback, | |
35 SBThreatType threat_type, | |
36 const ThreatMetadata& metadata) { | |
37 BrowserThread::PostTask( | |
38 BrowserThread::IO, FROM_HERE, | |
39 base::Bind(&SafeBrowsingApiHandler::URLCheckCallbackMeta::Run, | |
40 base::Owned(callback), threat_type, metadata)); | |
41 } | |
42 | |
43 void ReportUmaResult(safe_browsing::UmaRemoteCallResult result) { | |
44 UMA_HISTOGRAM_ENUMERATION("SB2.RemoteCall.Result", result, | |
45 safe_browsing::UMA_STATUS_MAX_VALUE); | |
46 } | |
47 | |
48 // Convert a SBThreatType to a Java threat type. We only support a few. | |
49 int SBThreatTypeToJavaThreatType(const SBThreatType& sb_threat_type) { | |
50 switch (sb_threat_type) { | |
51 case SB_THREAT_TYPE_URL_PHISHING: | |
52 return safe_browsing::JAVA_THREAT_TYPE_SOCIAL_ENGINEERING; | |
53 case SB_THREAT_TYPE_URL_MALWARE: | |
54 return safe_browsing::JAVA_THREAT_TYPE_POTENTIALLY_HARMFUL_APPLICATION; | |
55 default: | |
56 NOTREACHED(); | |
57 return 0; | |
58 } | |
59 } | |
60 | |
61 // Convert a vector of SBThreatTypes to JavaIntArray of Java threat types. | |
62 ScopedJavaLocalRef<jintArray> SBThreatTypesToJavaArray( | |
63 JNIEnv* env, | |
64 const std::vector<SBThreatType>& threat_types) { | |
65 DCHECK(threat_types.size() > 0); | |
66 int int_threat_types[threat_types.size()]; | |
67 int* itr = &int_threat_types[0]; | |
68 for (auto threat_type : threat_types) { | |
69 *itr++ = SBThreatTypeToJavaThreatType(threat_type); | |
70 } | |
71 return ToJavaIntArray(env, int_threat_types, threat_types.size()); | |
72 } | |
73 | |
74 } // namespace | |
75 | |
76 | |
77 bool RegisterSafeBrowsingApiBridge(JNIEnv* env) { | |
78 return RegisterNativesImpl(env); | |
79 } | |
80 | |
81 // Java->Native call, invoked when a check is done. | |
82 // |callback_id| is an int form of pointer to a URLCheckCallbackMeta | |
83 // that will be called and then deleted here. | |
84 // |result_status| is one of those from SafeBrowsingApiHandler.java | |
85 // |metadata| is a JSON string classifying the threat if there is one. | |
86 void OnUrlCheckDone(JNIEnv* env, | |
87 const JavaParamRef<jclass>& context, | |
88 jlong callback_id, | |
89 jint result_status, | |
90 const JavaParamRef<jstring>& metadata) { | |
91 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
92 DCHECK(callback_id); | |
93 | |
94 const std::string metadata_str = | |
95 (metadata ? ConvertJavaStringToUTF8(env, metadata) : ""); | |
96 | |
97 DVLOG(1) << "OnURLCheckDone invoked for check " << callback_id | |
98 << " with status=" << result_status << " and metadata=[" | |
99 << metadata_str << "]"; | |
100 | |
101 // Convert java long long int to c++ pointer, take ownership. | |
102 std::unique_ptr<SafeBrowsingApiHandler::URLCheckCallbackMeta> callback( | |
103 reinterpret_cast<SafeBrowsingApiHandlerBridge::URLCheckCallbackMeta*>( | |
104 callback_id)); | |
105 | |
106 if (result_status != RESULT_STATUS_SUCCESS) { | |
107 // TODO(nparker): If the API is consistently failing, we might want to | |
108 // turn it off altogether and retest periodically. This would | |
109 // alleviate a bad experience if GMSCore is somehow busted. | |
110 if (result_status == RESULT_STATUS_TIMEOUT) { | |
111 ReportUmaResult(UMA_STATUS_TIMEOUT); | |
112 VLOG(1) << "Safe browsing API call timed-out"; | |
113 } else { | |
114 DCHECK_EQ(result_status, RESULT_STATUS_INTERNAL_ERROR); | |
115 ReportUmaResult(UMA_STATUS_INTERNAL_ERROR); | |
116 LOG(WARNING) << "Safe browsing API had internal error"; | |
117 } | |
118 RunCallbackOnIOThread(callback.release(), SB_THREAT_TYPE_SAFE, | |
119 ThreatMetadata()); | |
120 return; | |
121 } | |
122 | |
123 // Shortcut for safe, so we don't have to parse JSON. | |
124 if (metadata_str == "{}") { | |
125 ReportUmaResult(UMA_STATUS_SAFE); | |
126 RunCallbackOnIOThread(callback.release(), SB_THREAT_TYPE_SAFE, | |
127 ThreatMetadata()); | |
128 } else { | |
129 // Unsafe, assuming we can parse the JSON. | |
130 SBThreatType worst_threat; | |
131 ThreatMetadata threat_metadata; | |
132 ReportUmaResult( | |
133 ParseJsonFromGMSCore(metadata_str, &worst_threat, &threat_metadata)); | |
134 if (worst_threat != SB_THREAT_TYPE_SAFE) { | |
135 DVLOG(1) << "Check " << callback_id << " marked as UNSAFE"; | |
136 } | |
137 | |
138 RunCallbackOnIOThread(callback.release(), worst_threat, threat_metadata); | |
139 } | |
140 } | |
141 | |
142 // | |
143 // SafeBrowsingApiHandlerBridge | |
144 // | |
145 SafeBrowsingApiHandlerBridge::SafeBrowsingApiHandlerBridge() | |
146 : checked_api_support_(false) {} | |
147 | |
148 SafeBrowsingApiHandlerBridge::~SafeBrowsingApiHandlerBridge() { | |
149 } | |
150 | |
151 bool SafeBrowsingApiHandlerBridge::CheckApiIsSupported() { | |
152 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
153 if (!checked_api_support_) { | |
154 DVLOG(1) << "Checking API support."; | |
155 j_api_handler_ = Java_SafeBrowsingApiBridge_create( | |
156 AttachCurrentThread(), GetApplicationContext()); | |
157 checked_api_support_ = true; | |
158 } | |
159 return j_api_handler_.obj() != nullptr; | |
160 } | |
161 | |
162 void SafeBrowsingApiHandlerBridge::StartURLCheck( | |
163 const SafeBrowsingApiHandler::URLCheckCallbackMeta& callback, | |
164 const GURL& url, | |
165 const std::vector<SBThreatType>& threat_types) { | |
166 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
167 | |
168 if (!CheckApiIsSupported()) { | |
169 // Mark all requests as safe. Only users who have an old, broken GMSCore or | |
170 // have sideloaded Chrome w/o PlayStore should land here. | |
171 RunCallbackOnIOThread(new URLCheckCallbackMeta(callback), | |
172 SB_THREAT_TYPE_SAFE, ThreatMetadata()); | |
173 ReportUmaResult(UMA_STATUS_UNSUPPORTED); | |
174 return; | |
175 } | |
176 | |
177 // Make copy on the heap so we can pass the pointer through JNI. | |
178 intptr_t callback_id = | |
179 reinterpret_cast<intptr_t>(new URLCheckCallbackMeta(callback)); | |
180 | |
181 DVLOG(1) << "Starting check " << callback_id << " for URL " << url; | |
182 | |
183 // Default threat types, to support upstream code that doesn't yet set them. | |
184 std::vector<SBThreatType> local_threat_types(threat_types); | |
185 if (local_threat_types.empty()) { | |
186 local_threat_types.push_back(SB_THREAT_TYPE_URL_PHISHING); | |
187 local_threat_types.push_back(SB_THREAT_TYPE_URL_MALWARE); | |
188 } | |
189 | |
190 JNIEnv* env = AttachCurrentThread(); | |
191 ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, url.spec()); | |
192 ScopedJavaLocalRef<jintArray> j_threat_types = | |
193 SBThreatTypesToJavaArray(env, local_threat_types); | |
194 | |
195 Java_SafeBrowsingApiBridge_startUriLookup(env, j_api_handler_, callback_id, | |
196 j_url, j_threat_types); | |
197 } | |
198 | |
199 } // namespace safe_browsing | |
OLD | NEW |