mirror of
https://github.com/github/codeql.git
synced 2026-05-04 21:25:44 +02:00
Merge pull request #6397 from atorralba/atorralba/android-intent-redirect-query
Java: Create new Android Intent Redirection query
This commit is contained in:
@@ -107,6 +107,7 @@ private module Frameworks {
|
||||
private import semmle.code.java.frameworks.spring.SpringBeans
|
||||
private import semmle.code.java.frameworks.spring.SpringWebMultipart
|
||||
private import semmle.code.java.frameworks.spring.SpringWebUtil
|
||||
private import semmle.code.java.security.AndroidIntentRedirection
|
||||
private import semmle.code.java.security.ResponseSplitting
|
||||
private import semmle.code.java.security.InformationLeak
|
||||
private import semmle.code.java.security.GroovyInjection
|
||||
|
||||
@@ -10,6 +10,11 @@ class TypeIntent extends Class {
|
||||
TypeIntent() { this.hasQualifiedName("android.content", "Intent") }
|
||||
}
|
||||
|
||||
/** The class `android.content.ComponentName`. */
|
||||
class TypeComponentName extends Class {
|
||||
TypeComponentName() { this.hasQualifiedName("android.content", "ComponentName") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The class `android.app.Activity`.
|
||||
*/
|
||||
@@ -293,3 +298,34 @@ private class IntentBundleFlowSteps extends SummaryModelCsv {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private class IntentComponentTaintSteps extends SummaryModelCsv {
|
||||
override predicate row(string s) {
|
||||
s =
|
||||
[
|
||||
"android.content;Intent;true;Intent;(Intent);;Argument[0];Argument[-1];taint",
|
||||
"android.content;Intent;true;Intent;(Context,Class);;Argument[1];Argument[-1];taint",
|
||||
"android.content;Intent;true;Intent;(String,Uri,Context,Class);;Argument[3];Argument[-1];taint",
|
||||
"android.content;Intent;true;getIntent;(String);;Argument[0];ReturnValue;taint",
|
||||
"android.content;Intent;true;getIntentOld;(String);;Argument[0];ReturnValue;taint",
|
||||
"android.content;Intent;true;parseUri;(String,int);;Argument[0];ReturnValue;taint",
|
||||
"android.content;Intent;true;setPackage;;;Argument[0];Argument[-1];taint",
|
||||
"android.content;Intent;true;setClass;;;Argument[1];Argument[-1];taint",
|
||||
"android.content;Intent;true;setClassName;(Context,String);;Argument[1];Argument[-1];taint",
|
||||
"android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint",
|
||||
"android.content;Intent;true;setComponent;;;Argument[0];Argument[-1];taint",
|
||||
"android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint",
|
||||
"android.content;ComponentName;false;ComponentName;(Context,String);;Argument[1];Argument[-1];taint",
|
||||
"android.content;ComponentName;false;ComponentName;(Context,Class);;Argument[1];Argument[-1];taint",
|
||||
"android.content;ComponentName;false;ComponentName;(Parcel);;Argument[0];Argument[-1];taint",
|
||||
"android.content;ComponentName;false;createRelative;(String,String);;Argument[0..1];ReturnValue;taint",
|
||||
"android.content;ComponentName;false;createRelative;(Context,String);;Argument[1];ReturnValue;taint",
|
||||
"android.content;ComponentName;false;flattenToShortString;;;Argument[-1];ReturnValue;taint",
|
||||
"android.content;ComponentName;false;flattenToString;;;Argument[-1];ReturnValue;taint",
|
||||
"android.content;ComponentName;false;getClassName;;;Argument[-1];ReturnValue;taint",
|
||||
"android.content;ComponentName;false;getPackageName;;;Argument[-1];ReturnValue;taint",
|
||||
"android.content;ComponentName;false;getShortClassName;;;Argument[-1];ReturnValue;taint",
|
||||
"android.content;ComponentName;false;unflattenFromString;;;Argument[0];ReturnValue;taint"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/** Provides classes to reason about Android Intent redirect vulnerabilities. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
|
||||
/**
|
||||
* A sink for Intent redirection vulnerabilities in Android,
|
||||
* that is, method calls that start Android components (like activities or services).
|
||||
*/
|
||||
abstract class IntentRedirectionSink extends DataFlow::Node { }
|
||||
|
||||
/** A sanitizer for data used to start an Android component. */
|
||||
abstract class IntentRedirectionSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A unit class for adding additional taint steps.
|
||||
*
|
||||
* Extend this class to add additional taint steps that should apply to `IntentRedirectionConfiguration`.
|
||||
*/
|
||||
class IntentRedirectionAdditionalTaintStep extends Unit {
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` should be considered a taint
|
||||
* step for the `IntentRedirectionConfiguration` configuration.
|
||||
*/
|
||||
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
||||
}
|
||||
|
||||
private class DefaultIntentRedirectionSinkModel extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"android.app;Activity;true;bindService;;;Argument[0];intent-start",
|
||||
"android.app;Activity;true;bindServiceAsUser;;;Argument[0];intent-start",
|
||||
"android.app;Activity;true;startActivityAsCaller;;;Argument[0];intent-start",
|
||||
"android.app;Activity;true;startActivityForResult;(Intent,int);;Argument[0];intent-start",
|
||||
"android.app;Activity;true;startActivityForResult;(Intent,int,Bundle);;Argument[0];intent-start",
|
||||
"android.app;Activity;true;startActivityForResult;(String,Intent,int,Bundle);;Argument[1];intent-start",
|
||||
"android.app;Activity;true;startActivityForResultAsUser;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;startActivities;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;startActivity;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;startActivityAsUser;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;startActivityFromChild;;;Argument[1];intent-start",
|
||||
"android.content;Context;true;startActivityFromFragment;;;Argument[1];intent-start",
|
||||
"android.content;Context;true;startActivityIfNeeded;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;startForegroundService;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;startService;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;startServiceAsUser;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;sendBroadcast;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;sendBroadcastAsUser;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;sendBroadcastWithMultiplePermissions;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;sendStickyBroadcast;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;sendStickyBroadcastAsUser;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;sendStickyOrderedBroadcast;;;Argument[0];intent-start",
|
||||
"android.content;Context;true;sendStickyOrderedBroadcastAsUser;;;Argument[0];intent-start"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** Default sink for Intent redirection vulnerabilities. */
|
||||
private class DefaultIntentRedirectionSink extends IntentRedirectionSink {
|
||||
DefaultIntentRedirectionSink() { sinkNode(this, "intent-start") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A default sanitizer for nodes dominated by calls to `ComponentName.getPackageName`
|
||||
* or `ComponentName.getClassName`. These are used to check whether the origin or destination
|
||||
* components are trusted.
|
||||
*/
|
||||
private class DefaultIntentRedirectionSanitizer extends IntentRedirectionSanitizer {
|
||||
DefaultIntentRedirectionSanitizer() {
|
||||
exists(MethodAccess ma, Method m, Guard g, boolean branch |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType() instanceof TypeComponentName and
|
||||
m.hasName(["getPackageName", "getClassName"]) and
|
||||
g.isEquality(ma, _, branch) and
|
||||
g.controls(this.asExpr().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/** Provides taint tracking configurations to be used in Android Intent redirection queries. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.DataFlow3
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.TaintTracking2
|
||||
import semmle.code.java.security.AndroidIntentRedirection
|
||||
|
||||
/**
|
||||
* A taint tracking configuration for tainted Intents being used to start Android components.
|
||||
*/
|
||||
class IntentRedirectionConfiguration extends TaintTracking::Configuration {
|
||||
IntentRedirectionConfiguration() { this = "IntentRedirectionConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectionSink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node sanitizer) {
|
||||
sanitizer instanceof IntentRedirectionSanitizer
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
any(IntentRedirectionAdditionalTaintStep c).step(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for sinks that receive the original incoming Intent,
|
||||
* since its component cannot be arbitrarily set.
|
||||
*/
|
||||
private class OriginalIntentSanitizer extends IntentRedirectionSanitizer {
|
||||
OriginalIntentSanitizer() { any(SameIntentBeingRelaunchedConfiguration c).hasFlowTo(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Data flow configuration used to discard incoming Intents
|
||||
* flowing directly to sinks that start Android components.
|
||||
*/
|
||||
private class SameIntentBeingRelaunchedConfiguration extends DataFlow3::Configuration {
|
||||
SameIntentBeingRelaunchedConfiguration() { this = "SameIntentBeingRelaunchedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectionSink }
|
||||
|
||||
override predicate isBarrier(DataFlow::Node barrier) {
|
||||
// Don't discard the Intent if its original component is tainted
|
||||
barrier instanceof IntentWithTaintedComponent
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// Intents being built with the copy constructor from the original Intent are discarded too
|
||||
exists(ClassInstanceExpr cie |
|
||||
cie.getConstructedType() instanceof TypeIntent and
|
||||
node1.asExpr() = cie.getArgument(0) and
|
||||
node1.asExpr().getType() instanceof TypeIntent and
|
||||
node2.asExpr() = cie
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An `Intent` with a tainted component. */
|
||||
private class IntentWithTaintedComponent extends DataFlow::Node {
|
||||
IntentWithTaintedComponent() {
|
||||
exists(IntentSetComponent setExpr, TaintedIntentComponentConf conf |
|
||||
setExpr.getQualifier() = this.asExpr() and
|
||||
conf.hasFlowTo(DataFlow::exprNode(setExpr.getSink()))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint tracking configuration for tainted data flowing to an `Intent`'s component.
|
||||
*/
|
||||
private class TaintedIntentComponentConf extends TaintTracking2::Configuration {
|
||||
TaintedIntentComponentConf() { this = "TaintedIntentComponentConf" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
any(IntentSetComponent setComponent).getSink() = sink.asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to a method that changes the component of an `Intent`. */
|
||||
private class IntentSetComponent extends MethodAccess {
|
||||
int sinkArg;
|
||||
|
||||
IntentSetComponent() {
|
||||
exists(Method m |
|
||||
this.getMethod() = m and
|
||||
m.getDeclaringType() instanceof TypeIntent
|
||||
|
|
||||
m.hasName("setClass") and
|
||||
sinkArg = 1
|
||||
or
|
||||
m.hasName("setClassName") and
|
||||
exists(Parameter p |
|
||||
p = m.getAParameter() and
|
||||
p.getType() instanceof TypeString and
|
||||
sinkArg = p.getPosition()
|
||||
)
|
||||
or
|
||||
m.hasName("setComponent") and
|
||||
sinkArg = 0
|
||||
or
|
||||
m.hasName("setPackage") and
|
||||
sinkArg = 0
|
||||
)
|
||||
}
|
||||
|
||||
Expr getSink() { result = this.getArgument(sinkArg) }
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>An exported Android component that obtains a user-provided Intent and uses it to launch another component
|
||||
can be exploited to obtain access to private, unexported components of the same app or to launch other apps' components
|
||||
on behalf of the victim app.</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Do not export components that start other components from a user-provided Intent.
|
||||
They can be made private by setting the <code>android:exported</code> property to <code>false</code> in the app's Android Manifest.</p>
|
||||
<p>If this is not possible, restrict either which apps can send Intents to the affected component, or which components can be started from it.</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following snippet contains three examples.
|
||||
In the first example, an arbitrary component can be started from the externally provided <code>forward_intent</code> Intent.
|
||||
In the second example, the destination component of the Intent is first checked to make sure it is safe.
|
||||
In the third example, the component that created the Intent is first checked to make sure it comes from a trusted origin.</p>
|
||||
<sample src="AndroidIntentRedirectionSample.java" />
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
Google:
|
||||
<a href="https://support.google.com/faqs/answer/9267555?hl=en">Remediation for Intent Redirection Vulnerability</a>.
|
||||
</li>
|
||||
<li>
|
||||
OWASP Mobile Security Testing Guide:
|
||||
<a href="https://mobile-security.gitbook.io/mobile-security-testing-guide/android-testing-guide/0x05a-platform-overview#intents">Intents</a>.
|
||||
</li>
|
||||
<li>
|
||||
Android Developers:
|
||||
<a href="https://developer.android.com/guide/topics/manifest/activity-element#exported">The android:exported attribute</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
24
java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
Normal file
24
java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @name Android Intent redirection
|
||||
* @description Starting Android components with user-provided Intents
|
||||
* can provide access to internal components of the application,
|
||||
* increasing the attack surface and potentially causing unintended effects.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @id java/android/intent-redirection
|
||||
* @tags security
|
||||
* external/cwe/cwe-926
|
||||
* external/cwe/cwe-940
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.AndroidIntentRedirectionQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, IntentRedirectionConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
"Arbitrary Android activities or services can be started from $@.", source.getNode(),
|
||||
"this user input"
|
||||
@@ -0,0 +1,18 @@
|
||||
// BAD: A user-provided Intent is used to launch an arbitrary component
|
||||
Intent forwardIntent = (Intent) getIntent().getParcelableExtra("forward_intent");
|
||||
startActivity(forwardIntent);
|
||||
|
||||
// GOOD: The destination component is checked before launching it
|
||||
Intent forwardIntent = (Intent) getIntent().getParcelableExtra("forward_intent");
|
||||
ComponentName destinationComponent = forwardIntent.resolveActivity(getPackageManager());
|
||||
if (destinationComponent.getPackageName().equals("safe.package") &&
|
||||
destinationComponent.getClassName().equals("SafeClass")) {
|
||||
startActivity(forwardIntent);
|
||||
}
|
||||
|
||||
// GOOD: The component that sent the Intent is checked before launching the destination component
|
||||
Intent forwardIntent = (Intent) getIntent().getParcelableExtra("forward_intent");
|
||||
ComponentName originComponent = getCallingActivity();
|
||||
if (originComponent.getPackageName().equals("trusted.package") && originComponent.getClassName().equals("TrustedClass")) {
|
||||
startActivity(forwardIntent);
|
||||
}
|
||||
@@ -29,7 +29,6 @@ public class IntentSources extends Activity {
|
||||
|
||||
}
|
||||
|
||||
|
||||
class OtherClass {
|
||||
|
||||
private static void sink(Object o) {}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package generatedtest;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
@@ -1597,6 +1598,167 @@ public class Test {
|
||||
out.readFromParcel(in);
|
||||
sink(getMapValue(out)); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;ComponentName;(Context,Class);;Argument[1];Argument[-1];taint"
|
||||
ComponentName out = null;
|
||||
Class in = (Class) source();
|
||||
out = new ComponentName((Context) null, in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;ComponentName;(Context,String);;Argument[1];Argument[-1];taint"
|
||||
ComponentName out = null;
|
||||
String in = (String) source();
|
||||
out = new ComponentName((Context) null, in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;ComponentName;(Parcel);;Argument[0];Argument[-1];taint"
|
||||
ComponentName out = null;
|
||||
Parcel in = (Parcel) source();
|
||||
out = new ComponentName(in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint"
|
||||
ComponentName out = null;
|
||||
String in = (String) source();
|
||||
out = new ComponentName(in, (String) null);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint"
|
||||
ComponentName out = null;
|
||||
String in = (String) source();
|
||||
out = new ComponentName((String) null, in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;createRelative;(Context,String);;Argument[1];ReturnValue;taint"
|
||||
ComponentName out = null;
|
||||
String in = (String) source();
|
||||
out = ComponentName.createRelative((Context) null, in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;createRelative;(String,String);;Argument[0..1];ReturnValue;taint"
|
||||
ComponentName out = null;
|
||||
String in = (String) source();
|
||||
out = ComponentName.createRelative(in, (String) null);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;createRelative;(String,String);;Argument[0..1];ReturnValue;taint"
|
||||
ComponentName out = null;
|
||||
String in = (String) source();
|
||||
out = ComponentName.createRelative((String) null, in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;flattenToShortString;;;Argument[-1];ReturnValue;taint"
|
||||
String out = null;
|
||||
ComponentName in = (ComponentName) source();
|
||||
out = in.flattenToShortString();
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;flattenToString;;;Argument[-1];ReturnValue;taint"
|
||||
String out = null;
|
||||
ComponentName in = (ComponentName) source();
|
||||
out = in.flattenToString();
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;getClassName;;;Argument[-1];ReturnValue;taint"
|
||||
String out = null;
|
||||
ComponentName in = (ComponentName) source();
|
||||
out = in.getClassName();
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;getPackageName;;;Argument[-1];ReturnValue;taint"
|
||||
String out = null;
|
||||
ComponentName in = (ComponentName) source();
|
||||
out = in.getPackageName();
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;getShortClassName;;;Argument[-1];ReturnValue;taint"
|
||||
String out = null;
|
||||
ComponentName in = (ComponentName) source();
|
||||
out = in.getShortClassName();
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;ComponentName;false;unflattenFromString;;;Argument[0];ReturnValue;taint"
|
||||
ComponentName out = null;
|
||||
String in = (String) source();
|
||||
out = ComponentName.unflattenFromString(in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;Intent;true;Intent;(Context,Class);;Argument[1];Argument[-1];taint"
|
||||
Intent out = null;
|
||||
Class in = (Class) source();
|
||||
out = new Intent((Context) null, in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;Intent;true;Intent;(Intent);;Argument[0];Argument[-1];taint"
|
||||
Intent out = null;
|
||||
Intent in = (Intent) source();
|
||||
out = new Intent(in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;Intent;true;Intent;(String,Uri,Context,Class);;Argument[3];Argument[-1];taint"
|
||||
Intent out = null;
|
||||
Class in = (Class) source();
|
||||
out = new Intent(null, null, null, in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;Intent;true;setClass;;;Argument[1];Argument[-1];taint"
|
||||
Intent out = null;
|
||||
Class in = (Class) source();
|
||||
out.setClass(null, in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;Intent;true;setClassName;(Context,String);;Argument[1];Argument[-1];taint"
|
||||
Intent out = null;
|
||||
String in = (String) source();
|
||||
out.setClassName((Context) null, in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint"
|
||||
Intent out = null;
|
||||
String in = (String) source();
|
||||
out.setClassName(in, (String) null);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint"
|
||||
Intent out = null;
|
||||
String in = (String) source();
|
||||
out.setClassName((String) null, in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;Intent;true;setComponent;;;Argument[0];Argument[-1];taint"
|
||||
Intent out = null;
|
||||
ComponentName in = (ComponentName) source();
|
||||
out.setComponent(in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
{
|
||||
// "android.content;Intent;true;setPackage;;;Argument[0];Argument[-1];taint"
|
||||
Intent out = null;
|
||||
String in = (String) source();
|
||||
out.setPackage(in);
|
||||
sink(out); // $ hasTaintFlow
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
package com.example.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class AndroidIntentRedirectionTest extends Activity {
|
||||
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
|
||||
|
||||
// @formatter:off
|
||||
startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirection
|
||||
startActivities(new Intent[] {intent}, null); // $ hasAndroidIntentRedirection
|
||||
startActivity(intent); // $ hasAndroidIntentRedirection
|
||||
startActivity(intent, null); // $ hasAndroidIntentRedirection
|
||||
startActivityAsUser(intent, null); // $ hasAndroidIntentRedirection
|
||||
startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirection
|
||||
startActivityForResult(intent, 0); // $ hasAndroidIntentRedirection
|
||||
startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirection
|
||||
startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirection
|
||||
startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirection
|
||||
startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirection
|
||||
startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirection
|
||||
bindService(intent, null, 0);
|
||||
bindServiceAsUser(intent, null, 0, null);
|
||||
startService(intent); // $ hasAndroidIntentRedirection
|
||||
startServiceAsUser(intent, null); // $ hasAndroidIntentRedirection
|
||||
startForegroundService(intent); // $ hasAndroidIntentRedirection
|
||||
sendBroadcast(intent); // $ hasAndroidIntentRedirection
|
||||
sendBroadcast(intent, null); // $ hasAndroidIntentRedirection
|
||||
sendBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
|
||||
sendBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
|
||||
sendBroadcastWithMultiplePermissions(intent, null); // $ hasAndroidIntentRedirection
|
||||
sendStickyBroadcast(intent); // $ hasAndroidIntentRedirection
|
||||
sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
|
||||
sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirection
|
||||
sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirection
|
||||
// @formatter:on
|
||||
|
||||
if (intent.getComponent().getPackageName().equals("something")) {
|
||||
startActivity(intent); // Safe - sanitized
|
||||
} else {
|
||||
startActivity(intent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
if (intent.getComponent().getClassName().equals("something")) {
|
||||
startActivity(intent); // Safe - sanitized
|
||||
} else {
|
||||
startActivity(intent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
|
||||
try {
|
||||
{
|
||||
// Delayed cast
|
||||
Object obj = getIntent().getParcelableExtra("forward_intent");
|
||||
Intent fwdIntent = (Intent) obj;
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
fwdIntent.setClassName((Context) null, intent.getStringExtra("className"));
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
fwdIntent.setClassName(intent.getStringExtra("packageName"), null);
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
fwdIntent.setClassName(intent.getStringExtra("packageName"),
|
||||
intent.getStringExtra("className"));
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
fwdIntent.setClass(null, Class.forName(intent.getStringExtra("className")));
|
||||
// needs taint step for Class.forName
|
||||
startActivity(fwdIntent); // $ MISSING: $hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
fwdIntent.setPackage(intent.getStringExtra("packageName"));
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
ComponentName component =
|
||||
new ComponentName(intent.getStringExtra("packageName"), null);
|
||||
fwdIntent.setComponent(component);
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
ComponentName component =
|
||||
new ComponentName("", intent.getStringExtra("className"));
|
||||
fwdIntent.setComponent(component);
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
ComponentName component =
|
||||
new ComponentName((Context) null, intent.getStringExtra("className"));
|
||||
fwdIntent.setComponent(component);
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
ComponentName component = new ComponentName((Context) null,
|
||||
Class.forName(intent.getStringExtra("className")));
|
||||
fwdIntent.setComponent(component);
|
||||
// needs taint step for Class.forName
|
||||
startActivity(fwdIntent); // $ MISSING: $hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
ComponentName component =
|
||||
ComponentName.createRelative("", intent.getStringExtra("className"));
|
||||
fwdIntent.setComponent(component);
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
ComponentName component =
|
||||
ComponentName.createRelative(intent.getStringExtra("packageName"), "");
|
||||
fwdIntent.setComponent(component);
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = new Intent();
|
||||
ComponentName component = ComponentName.createRelative((Context) null,
|
||||
intent.getStringExtra("className"));
|
||||
fwdIntent.setComponent(component);
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent originalIntent = getIntent();
|
||||
ComponentName cp = new ComponentName(originalIntent.getStringExtra("packageName"),
|
||||
originalIntent.getStringExtra("className"));
|
||||
Intent anotherIntent = new Intent();
|
||||
anotherIntent.setComponent(cp);
|
||||
startActivity(originalIntent); // Safe - not a tainted Intent
|
||||
}
|
||||
{
|
||||
Intent originalIntent = getIntent();
|
||||
Intent anotherIntent = new Intent(originalIntent);
|
||||
startActivity(anotherIntent); // Safe - copy constructor from original Intent
|
||||
}
|
||||
{
|
||||
Intent originalIntent = getIntent();
|
||||
Intent fwdIntent = (Intent) originalIntent.getParcelableExtra("forward_intent");
|
||||
if (originalIntent.getBooleanExtra("use_fwd_intent", false)) {
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
} else {
|
||||
startActivity(originalIntent); // Safe - not an Intent obtained from the Extras
|
||||
}
|
||||
}
|
||||
{
|
||||
Intent originalIntent = getIntent();
|
||||
originalIntent.setClassName(originalIntent.getStringExtra("package_name"),
|
||||
originalIntent.getStringExtra("class_name"));
|
||||
startActivity(originalIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent originalIntent = getIntent();
|
||||
originalIntent.setClassName("not_user_provided", "not_user_provided");
|
||||
startActivity(originalIntent); // Safe - component changed but not tainted
|
||||
}
|
||||
{
|
||||
Intent originalIntent = getIntent();
|
||||
Intent fwdIntent;
|
||||
if (originalIntent.getBooleanExtra("use_fwd_intent", false)) {
|
||||
fwdIntent = (Intent) originalIntent.getParcelableExtra("forward_intent");
|
||||
} else {
|
||||
fwdIntent = originalIntent;
|
||||
}
|
||||
// Conditionally tainted sinks aren't supported currently
|
||||
startActivity(fwdIntent); // $ MISSING: $hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = Intent.parseUri(getIntent().getStringExtra("uri"), 0);
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = Intent.getIntent(getIntent().getStringExtra("uri"));
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
{
|
||||
Intent fwdIntent = Intent.getIntentOld(getIntent().getStringExtra("uri"));
|
||||
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import java
|
||||
import semmle.code.java.security.AndroidIntentRedirectionQuery
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class HasAndroidIntentRedirectionTest extends InlineExpectationsTest {
|
||||
HasAndroidIntentRedirectionTest() { this = "HasAndroidIntentRedirectionTest" }
|
||||
|
||||
override string getARelevantTag() { result = "hasAndroidIntentRedirection" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "hasAndroidIntentRedirection" and
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, IntentRedirectionConfiguration conf |
|
||||
conf.hasFlow(src, sink)
|
||||
|
|
||||
sink.getLocation() = location and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.app"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="1"
|
||||
android:versionName="0.1" >
|
||||
|
||||
<application
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" >
|
||||
<activity
|
||||
android:name=".AndroidIntentRedirectionTest"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".SafeActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
1
java/ql/test/query-tests/security/CWE-940/options
Normal file
1
java/ql/test/query-tests/security/CWE-940/options
Normal file
@@ -0,0 +1 @@
|
||||
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0
|
||||
19
java/ql/test/stubs/google-android-9.0.0/android/annotation/NonNull.java
generated
Normal file
19
java/ql/test/stubs/google-android-9.0.0/android/annotation/NonNull.java
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 android.annotation;
|
||||
|
||||
public @interface NonNull {
|
||||
}
|
||||
42
java/ql/test/stubs/google-android-9.0.0/android/annotation/RequiresPermission.java
generated
Normal file
42
java/ql/test/stubs/google-android-9.0.0/android/annotation/RequiresPermission.java
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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 android.annotation;
|
||||
|
||||
import java.lang.annotation.Target;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
|
||||
public @interface RequiresPermission {
|
||||
String value() default "";
|
||||
|
||||
String[] allOf() default {};
|
||||
|
||||
String[] anyOf() default {};
|
||||
|
||||
boolean conditional() default false;
|
||||
|
||||
@Target({FIELD, METHOD, PARAMETER})
|
||||
|
||||
@interface Read {
|
||||
RequiresPermission value() default @RequiresPermission;
|
||||
|
||||
}
|
||||
@Target({FIELD, METHOD, PARAMETER})
|
||||
|
||||
@interface Write {
|
||||
RequiresPermission value() default @RequiresPermission;
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
67
java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java
generated
Normal file
67
java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java
generated
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 android.app;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
|
||||
public class Fragment {
|
||||
|
||||
public static class SavedState implements Parcelable {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static public class InstantiationException {
|
||||
public InstantiationException(String msg, Exception cause) {}
|
||||
|
||||
}
|
||||
|
||||
public Fragment() {}
|
||||
|
||||
public static Fragment instantiate(Context context, String fname) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
final public boolean equals(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
final public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
package android.content;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
|
||||
@@ -2,15 +2,11 @@
|
||||
|
||||
package android.content;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentCallbacks;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentSender;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.Executor;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
@@ -29,11 +25,6 @@ import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Display;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
abstract public class Context
|
||||
{
|
||||
@@ -45,6 +36,7 @@ abstract public class Context
|
||||
public abstract ClassLoader getClassLoader();
|
||||
public abstract ComponentName startForegroundService(Intent p0);
|
||||
public abstract ComponentName startService(Intent p0);
|
||||
public abstract ComponentName startServiceAsUser(Intent p0, UserHandle p1);
|
||||
public abstract ContentResolver getContentResolver();
|
||||
public abstract Context createConfigurationContext(Configuration p0);
|
||||
public abstract Context createContextForSplit(String p0);
|
||||
@@ -90,6 +82,7 @@ abstract public class Context
|
||||
public abstract String[] databaseList();
|
||||
public abstract String[] fileList();
|
||||
public abstract boolean bindService(Intent p0, ServiceConnection p1, int p2);
|
||||
public abstract boolean bindServiceAsUser(Intent p0, ServiceConnection p1, int p2, UserHandle p3);
|
||||
public abstract boolean deleteDatabase(String p0);
|
||||
public abstract boolean deleteFile(String p0);
|
||||
public abstract boolean deleteSharedPreferences(String p0);
|
||||
@@ -141,6 +134,7 @@ abstract public class Context
|
||||
public abstract void startActivities(Intent[] p0, Bundle p1);
|
||||
public abstract void startActivity(Intent p0);
|
||||
public abstract void startActivity(Intent p0, Bundle p1);
|
||||
public abstract void startActivityAsUser(Intent intent, UserHandle user);
|
||||
public abstract void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4);
|
||||
public abstract void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4, Bundle p5);
|
||||
public abstract void unbindService(ServiceConnection p0);
|
||||
|
||||
@@ -15,22 +15,16 @@
|
||||
*/
|
||||
package android.content;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentCallbacks;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentSender;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.Executor;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.DatabaseErrorHandler;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -40,13 +34,7 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Display;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Proxying implementation of Context that simply delegates all of its calls to
|
||||
@@ -54,7 +42,15 @@ import java.util.concurrent.Executor;
|
||||
* the original Context.
|
||||
*/
|
||||
public class ContextWrapper extends Context {
|
||||
public ContextWrapper() {
|
||||
public ContextWrapper() {}
|
||||
|
||||
public Context getBaseContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getMainExecutor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ContextWrapper(Context base) {
|
||||
@@ -75,6 +71,7 @@ public class ContextWrapper extends Context {
|
||||
@Override public ClassLoader getClassLoader() { return null; }
|
||||
@Override public ComponentName startForegroundService(Intent p0) { return null; }
|
||||
@Override public ComponentName startService(Intent p0) { return null; }
|
||||
@Override public ComponentName startServiceAsUser(Intent p0, UserHandle p1) { return null; }
|
||||
@Override public ContentResolver getContentResolver() { return null; }
|
||||
@Override public Context createConfigurationContext(Configuration p0) { return null; }
|
||||
@Override public Context createContextForSplit(String p0) { return null; }
|
||||
@@ -120,6 +117,7 @@ public class ContextWrapper extends Context {
|
||||
@Override public String[] databaseList() { return null; }
|
||||
@Override public String[] fileList() { return null; }
|
||||
@Override public boolean bindService(Intent p0, ServiceConnection p1, int p2) { return false; }
|
||||
@Override public boolean bindServiceAsUser(Intent p0, ServiceConnection p1, int p2, UserHandle p3) { return false; }
|
||||
@Override public boolean deleteDatabase(String p0) { return false; }
|
||||
@Override public boolean deleteFile(String p0) { return false; }
|
||||
@Override public boolean deleteSharedPreferences(String p0) { return false; }
|
||||
@@ -171,6 +169,7 @@ public class ContextWrapper extends Context {
|
||||
@Override public void startActivities(Intent[] p0, Bundle p1) { }
|
||||
@Override public void startActivity(Intent p0) { }
|
||||
@Override public void startActivity(Intent p0, Bundle p1) { }
|
||||
@Override public void startActivityAsUser(Intent p0, UserHandle p1) { }
|
||||
@Override public void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4) { }
|
||||
@Override public void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4, Bundle p5) { }
|
||||
@Override public void unbindService(ServiceConnection p0) { }
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
package android.content;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.IntentSender;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
@@ -16,10 +15,6 @@ import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
public class Intent implements Cloneable, Parcelable
|
||||
{
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
|
||||
package android.content;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.PatternMatcher;
|
||||
import android.util.AndroidException;
|
||||
import android.util.Printer;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
public class IntentFilter implements Parcelable
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
package android.content;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.os.IBinder;
|
||||
|
||||
public interface ServiceConnection
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Printer;
|
||||
|
||||
public class Handler
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class UserHandle implements Parcelable
|
||||
{
|
||||
protected UserHandle() {}
|
||||
|
||||
@@ -1,602 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
* Copyright (C) 2008 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
|
||||
* 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
|
||||
* 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.
|
||||
* 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 android.webkit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A View that displays web pages. This class is the basis upon which you can
|
||||
* roll your own web browser or simply display some online content within your
|
||||
* Activity. It uses the WebKit rendering engine to display web pages and
|
||||
* includes methods to navigate forward and backward through a history, zoom in
|
||||
* and out, perform text searches and more.
|
||||
*
|
||||
* <p>
|
||||
* Note that, in order for your Activity to access the Internet and load web
|
||||
* pages in a WebView, you must add the {@code INTERNET} permissions to your
|
||||
* Android Manifest file:
|
||||
*
|
||||
* <pre>
|
||||
* {@code <uses-permission android:name="android.permission.INTERNET" />}
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This must be a child of the <a href="
|
||||
* {@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
|
||||
* element.
|
||||
*
|
||||
* <p>
|
||||
* For more information, read
|
||||
* <a href="{@docRoot}guide/webapps/webview.html">Building Web Apps in
|
||||
* WebView</a>.
|
||||
*
|
||||
* <h3>Basic usage</h3>
|
||||
*
|
||||
* <p>
|
||||
* By default, a WebView provides no browser-like widgets, does not enable
|
||||
* JavaScript and web page errors are ignored. If your goal is only to display
|
||||
* some HTML as a part of your UI, this is probably fine; the user won't need to
|
||||
* interact with the web page beyond reading it, and the web page won't need to
|
||||
* interact with the user. If you actually want a full-blown web browser, then
|
||||
* you probably want to invoke the Browser application with a URL Intent rather
|
||||
* than show it with a WebView. For example:
|
||||
*
|
||||
* <pre>
|
||||
* Uri uri = Uri.parse("https://www.example.com");
|
||||
* Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
* startActivity(intent);
|
||||
* </pre>
|
||||
* <p>
|
||||
* See {@link android.content.Intent} for more information.
|
||||
*
|
||||
* <p>
|
||||
* To provide a WebView in your own Activity, include a {@code <WebView>} in
|
||||
* your layout, or set the entire Activity window as a WebView during
|
||||
* {@link android.app.Activity#onCreate(Bundle) onCreate()}:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* WebView webview = new WebView(this);
|
||||
* setContentView(webview);
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Then load the desired web page:
|
||||
*
|
||||
* <pre>
|
||||
* // Simplest usage: note that an exception will NOT be thrown
|
||||
* // if there is an error loading this page (see below).
|
||||
* webview.loadUrl("https://example.com/");
|
||||
*
|
||||
* // OR, you can also load from an HTML string:
|
||||
* String summary = "<html><body>You scored <b>192</b> points.</body></html>";
|
||||
* webview.loadData(summary, "text/html", null);
|
||||
* // ... although note that there are restrictions on what this HTML can do.
|
||||
* // See {@link #loadData(String,String,String)} and {@link
|
||||
* #loadDataWithBaseURL(String,String,String,String,String)} for more info.
|
||||
* // Also see {@link #loadData(String,String,String)} for information on encoding special
|
||||
* // characters.
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* A WebView has several customization points where you can add your own
|
||||
* behavior. These are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
|
||||
* This class is called when something that might impact a browser UI happens,
|
||||
* for instance, progress updates and JavaScript alerts are sent here (see
|
||||
* <a href="
|
||||
* {@docRoot}guide/developing/debug-tasks.html#DebuggingWebPages">Debugging
|
||||
* Tasks</a>).</li>
|
||||
* <li>Creating and setting a {@link android.webkit.WebViewClient} subclass. It
|
||||
* will be called when things happen that impact the rendering of the content,
|
||||
* eg, errors or form submissions. You can also intercept URL loading here (via
|
||||
* {@link android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
|
||||
* shouldOverrideUrlLoading()}).</li>
|
||||
* <li>Modifying the {@link android.webkit.WebSettings}, such as enabling
|
||||
* JavaScript with
|
||||
* {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)
|
||||
* setJavaScriptEnabled()}.</li>
|
||||
* <li>Injecting Java objects into the WebView using the
|
||||
* {@link android.webkit.WebView#addJavascriptInterface} method. This method
|
||||
* allows you to inject Java objects into a page's JavaScript context, so that
|
||||
* they can be accessed by JavaScript in the page.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Here's a more complicated example, showing error handling, settings, and
|
||||
* progress notification:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* // Let's display the progress in the activity title bar, like the
|
||||
* // browser app does.
|
||||
* getWindow().requestFeature(Window.FEATURE_PROGRESS);
|
||||
*
|
||||
* webview.getSettings().setJavaScriptEnabled(true);
|
||||
*
|
||||
* final Activity activity = this;
|
||||
* webview.setWebChromeClient(new WebChromeClient() {
|
||||
* public void onProgressChanged(WebView view, int progress) {
|
||||
* // Activities and WebViews measure progress with different scales.
|
||||
* // The progress meter will automatically disappear when we reach 100%
|
||||
* activity.setProgress(progress * 1000);
|
||||
* }
|
||||
* });
|
||||
* webview.setWebViewClient(new WebViewClient() {
|
||||
* public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
||||
* Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* webview.loadUrl("https://developer.android.com/");
|
||||
* </pre>
|
||||
*
|
||||
* <h3>Zoom</h3>
|
||||
*
|
||||
* <p>
|
||||
* To enable the built-in zoom, set {@link #getSettings()
|
||||
* WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)} (introduced
|
||||
* in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}).
|
||||
*
|
||||
* <p class="note">
|
||||
* <b>Note:</b> Using zoom if either the height or width is set to
|
||||
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to
|
||||
* undefined behavior and should be avoided.
|
||||
*
|
||||
* <h3>Cookie and window management</h3>
|
||||
*
|
||||
* <p>
|
||||
* For obvious security reasons, your application has its own cache, cookie
|
||||
* store etc.—it does not share the Browser application's data.
|
||||
*
|
||||
* <p>
|
||||
* By default, requests by the HTML to open new windows are ignored. This is
|
||||
* {@code true} whether they be opened by JavaScript or by the target attribute
|
||||
* on a link. You can customize your {@link WebChromeClient} to provide your own
|
||||
* behavior for opening multiple windows, and render them in whatever manner you
|
||||
* want.
|
||||
*
|
||||
* <p>
|
||||
* The standard behavior for an Activity is to be destroyed and recreated when
|
||||
* the device orientation or any other configuration changes. This will cause
|
||||
* the WebView to reload the current page. If you don't want that, you can set
|
||||
* your Activity to handle the {@code orientation} and {@code keyboardHidden}
|
||||
* changes, and then just leave the WebView alone. It'll automatically re-orient
|
||||
* itself as appropriate. Read
|
||||
* <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling
|
||||
* Runtime Changes</a> for more information about how to handle configuration
|
||||
* changes during runtime.
|
||||
*
|
||||
*
|
||||
* <h3>Building web pages to support different screen densities</h3>
|
||||
*
|
||||
* <p>
|
||||
* The screen density of a device is based on the screen resolution. A screen
|
||||
* with low density has fewer available pixels per inch, where a screen with
|
||||
* high density has more — sometimes significantly more — pixels per
|
||||
* inch. The density of a screen is important because, other things being equal,
|
||||
* a UI element (such as a button) whose height and width are defined in terms
|
||||
* of screen pixels will appear larger on the lower density screen and smaller
|
||||
* on the higher density screen. For simplicity, Android collapses all actual
|
||||
* screen densities into three generalized densities: high, medium, and low.
|
||||
* <p>
|
||||
* By default, WebView scales a web page so that it is drawn at a size that
|
||||
* matches the default appearance on a medium density screen. So, it applies
|
||||
* 1.5x scaling on a high density screen (because its pixels are smaller) and
|
||||
* 0.75x scaling on a low density screen (because its pixels are bigger).
|
||||
* Starting with API level {@link android.os.Build.VERSION_CODES#ECLAIR},
|
||||
* WebView supports DOM, CSS, and meta tag features to help you (as a web
|
||||
* developer) target screens with different screen densities.
|
||||
* <p>
|
||||
* Here's a summary of the features you can use to handle different screen
|
||||
* densities:
|
||||
* <ul>
|
||||
* <li>The {@code window.devicePixelRatio} DOM property. The value of this
|
||||
* property specifies the default scaling factor used for the current device.
|
||||
* For example, if the value of {@code
|
||||
* window.devicePixelRatio} is "1.0", then the device is considered a medium
|
||||
* density (mdpi) device and default scaling is not applied to the web page; if
|
||||
* the value is "1.5", then the device is considered a high density device
|
||||
* (hdpi) and the page content is scaled 1.5x; if the value is "0.75", then the
|
||||
* device is considered a low density device (ldpi) and the content is scaled
|
||||
* 0.75x.</li>
|
||||
* <li>The {@code -webkit-device-pixel-ratio} CSS media query. Use this to
|
||||
* specify the screen densities for which this style sheet is to be used. The
|
||||
* corresponding value should be either "0.75", "1", or "1.5", to indicate that
|
||||
* the styles are for devices with low density, medium density, or high density
|
||||
* screens, respectively. For example:
|
||||
*
|
||||
* <pre>
|
||||
* <link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" />
|
||||
* </pre>
|
||||
* <p>
|
||||
* The {@code hdpi.css} stylesheet is only used for devices with a screen pixel
|
||||
* ratio of 1.5, which is the high density pixel ratio.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>HTML5 Video support</h3>
|
||||
*
|
||||
* <p>
|
||||
* In order to support inline HTML5 video in your application you need to have
|
||||
* hardware acceleration turned on.
|
||||
*
|
||||
* <h3>Full screen support</h3>
|
||||
*
|
||||
* <p>
|
||||
* In order to support full screen — for video or other HTML content
|
||||
* — you need to set a {@link android.webkit.WebChromeClient} and
|
||||
* implement both
|
||||
* {@link WebChromeClient#onShowCustomView(View, WebChromeClient.CustomViewCallback)}
|
||||
* and {@link WebChromeClient#onHideCustomView()}. If the implementation of
|
||||
* either of these two methods is missing then the web contents will not be
|
||||
* allowed to enter full screen. Optionally you can implement
|
||||
* {@link WebChromeClient#getVideoLoadingProgressView()} to customize the View
|
||||
* displayed whilst a video is loading.
|
||||
*
|
||||
* <h3>HTML5 Geolocation API support</h3>
|
||||
*
|
||||
* <p>
|
||||
* For applications targeting Android N and later releases (API level >
|
||||
* {@link android.os.Build.VERSION_CODES#M}) the geolocation api is only
|
||||
* supported on secure origins such as https. For such applications requests to
|
||||
* geolocation api on non-secure origins are automatically denied without
|
||||
* invoking the corresponding
|
||||
* {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)}
|
||||
* method.
|
||||
*
|
||||
* <h3>Layout size</h3>
|
||||
* <p>
|
||||
* It is recommended to set the WebView layout height to a fixed value or to
|
||||
* {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} instead of using
|
||||
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}. When using
|
||||
* {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} for the height none
|
||||
* of the WebView's parents should use a
|
||||
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} layout height since
|
||||
* that could result in incorrect sizing of the views.
|
||||
*
|
||||
* <p>
|
||||
* Setting the WebView's height to
|
||||
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} enables the
|
||||
* following behaviors:
|
||||
* <ul>
|
||||
* <li>The HTML body layout height is set to a fixed value. This means that
|
||||
* elements with a height relative to the HTML body may not be sized correctly.
|
||||
* </li>
|
||||
* <li>For applications targeting {@link android.os.Build.VERSION_CODES#KITKAT}
|
||||
* and earlier SDKs the HTML viewport meta tag will be ignored in order to
|
||||
* preserve backwards compatibility.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Using a layout width of
|
||||
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} is not supported. If
|
||||
* such a width is used the WebView will attempt to use the width of the parent
|
||||
* instead.
|
||||
*
|
||||
* <h3>Metrics</h3>
|
||||
*
|
||||
* <p>
|
||||
* WebView may upload anonymous diagnostic data to Google when the user has
|
||||
* consented. This data helps Google improve WebView. Data is collected on a
|
||||
* per-app basis for each app which has instantiated a WebView. An individual
|
||||
* app can opt out of this feature by putting the following tag in its
|
||||
* manifest's {@code <application>} element:
|
||||
*
|
||||
* <pre>
|
||||
* <manifest>
|
||||
* <application>
|
||||
* ...
|
||||
* <meta-data android:name="android.webkit.WebView.MetricsOptOut"
|
||||
* android:value="true" />
|
||||
* </application>
|
||||
* </manifest>
|
||||
* </pre>
|
||||
* <p>
|
||||
* Data will only be uploaded for a given app if the user has consented AND the
|
||||
* app has not opted out.
|
||||
*
|
||||
* <h3>Safe Browsing</h3>
|
||||
*
|
||||
* <p>
|
||||
* With Safe Browsing, WebView will block malicious URLs and present a warning
|
||||
* UI to the user to allow them to navigate back safely or proceed to the
|
||||
* malicious page.
|
||||
* <p>
|
||||
* Safe Browsing is enabled by default on devices which support it. If your app
|
||||
* needs to disable Safe Browsing for all WebViews, it can do so in the
|
||||
* manifest's {@code <application>} element:
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <manifest>
|
||||
* <application>
|
||||
* ...
|
||||
* <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
|
||||
* android:value="false" />
|
||||
* </application>
|
||||
* </manifest>
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Otherwise, see {@link WebSettings#setSafeBrowsingEnabled}.
|
||||
*
|
||||
*/
|
||||
// Implementation notes.
|
||||
// The WebView is a thin API class that delegates its public API to a backend
|
||||
// WebViewProvider
|
||||
// class instance. WebView extends {@link AbsoluteLayout} for backward
|
||||
// compatibility reasons.
|
||||
// Methods are delegated to the provider implementation: all public API methods
|
||||
// introduced in this
|
||||
// file are fully delegated, whereas public and protected methods from the View
|
||||
// base classes are
|
||||
// only delegated where a specific need exists for them to do so.
|
||||
public class WebView {
|
||||
|
||||
/**
|
||||
* Constructs a new WebView with an Activity Context object.
|
||||
*
|
||||
* <p class="note">
|
||||
* <b>Note:</b> WebView should always be instantiated with an Activity Context.
|
||||
* If instantiated with an Application Context, WebView will be unable to
|
||||
* provide several features, such as JavaScript dialogs and autofill.
|
||||
*
|
||||
* @param context an Activity Context to access application assets
|
||||
*/
|
||||
public class WebView extends View {
|
||||
public WebView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given URL with the specified additional HTTP headers.
|
||||
* <p>
|
||||
* Also see compatibility note on {@link #evaluateJavascript}.
|
||||
*
|
||||
* @param url the URL of the resource to load
|
||||
* @param additionalHttpHeaders the additional headers to be used in the HTTP
|
||||
* request for this URL, specified as a map from
|
||||
* name to value. Note that if this map contains
|
||||
* any of the headers that are set by default by
|
||||
* this WebView, such as those controlling caching,
|
||||
* accept types or the User-Agent, their values may
|
||||
* be overridden by this WebView's defaults.
|
||||
*/
|
||||
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
|
||||
public void setHorizontalScrollbarOverlay(boolean overlay) {}
|
||||
|
||||
public void setVerticalScrollbarOverlay(boolean overlay) {}
|
||||
|
||||
public boolean overlayHorizontalScrollbar() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given URL.
|
||||
* <p>
|
||||
* Also see compatibility note on {@link #evaluateJavascript}.
|
||||
*
|
||||
* @param url the URL of the resource to load
|
||||
*/
|
||||
public void loadUrl(String url) {
|
||||
public boolean overlayVerticalScrollbar() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the URL with postData using "POST" method into this WebView. If url is
|
||||
* not a network URL, it will be loaded with {@link #loadUrl(String)} instead,
|
||||
* ignoring the postData param.
|
||||
*
|
||||
* @param url the URL of the resource to load
|
||||
* @param postData the data will be passed to "POST" request, which must be be
|
||||
* "application/x-www-form-urlencoded" encoded.
|
||||
*/
|
||||
public void postUrl(String url, byte[] postData) {
|
||||
}
|
||||
public void savePassword(String host, String username, String password) {}
|
||||
|
||||
/**
|
||||
* Loads the given data into this WebView using a 'data' scheme URL.
|
||||
* <p>
|
||||
* Note that JavaScript's same origin policy means that script running in a page
|
||||
* loaded using this method will be unable to access content loaded using any
|
||||
* scheme other than 'data', including 'http(s)'. To avoid this restriction, use
|
||||
* {@link #loadDataWithBaseURL(String,String,String,String,String)
|
||||
* loadDataWithBaseURL()} with an appropriate base URL.
|
||||
* <p>
|
||||
* The {@code encoding} parameter specifies whether the data is base64 or URL
|
||||
* encoded. If the data is base64 encoded, the value of the encoding parameter
|
||||
* must be 'base64'. HTML can be encoded with
|
||||
* {@link android.util.Base64#encodeToString(byte[],int)} like so:
|
||||
*
|
||||
* <pre>
|
||||
* String unencodedHtml = "<html><body>'%28' is the code for '('</body></html>";
|
||||
* String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING);
|
||||
* webView.loadData(encodedHtml, "text/html", "base64");
|
||||
* </pre>
|
||||
* <p>
|
||||
* For all other values of {@code encoding} (including {@code null}) it is
|
||||
* assumed that the data uses ASCII encoding for octets inside the range of safe
|
||||
* URL characters and use the standard %xx hex encoding of URLs for octets
|
||||
* outside that range. See
|
||||
* <a href="https://tools.ietf.org/html/rfc3986#section-2.2">RFC 3986</a> for
|
||||
* more information.
|
||||
* <p>
|
||||
* The {@code mimeType} parameter specifies the format of the data. If WebView
|
||||
* can't handle the specified MIME type, it will download the data. If
|
||||
* {@code null}, defaults to 'text/html'.
|
||||
* <p>
|
||||
* The 'data' scheme URL formed by this method uses the default US-ASCII
|
||||
* charset. If you need to set a different charset, you should form a 'data'
|
||||
* scheme URL which explicitly specifies a charset parameter in the mediatype
|
||||
* portion of the URL and call {@link #loadUrl(String)} instead. Note that the
|
||||
* charset obtained from the mediatype portion of a data URL always overrides
|
||||
* that specified in the HTML or XML document itself.
|
||||
* <p>
|
||||
* Content loaded using this method will have a {@code window.origin} value of
|
||||
* {@code "null"}. This must not be considered to be a trusted origin by the
|
||||
* application or by any JavaScript code running inside the WebView (for
|
||||
* example, event sources in DOM event handlers or web messages), because
|
||||
* malicious content can also create frames with a null origin. If you need to
|
||||
* identify the main frame's origin in a trustworthy way, you should use
|
||||
* {@link #loadDataWithBaseURL(String,String,String,String,String)
|
||||
* loadDataWithBaseURL()} with a valid HTTP or HTTPS base URL to set the origin.
|
||||
*
|
||||
* @param data a String of data in the given encoding
|
||||
* @param mimeType the MIME type of the data, e.g. 'text/html'.
|
||||
* @param encoding the encoding of the data
|
||||
*/
|
||||
public void loadData(String data, String mimeType, String encoding) {
|
||||
}
|
||||
public void setHttpAuthUsernamePassword(String host, String realm, String username,
|
||||
String password) {}
|
||||
|
||||
/**
|
||||
* Loads the given data into this WebView, using baseUrl as the base URL for the
|
||||
* content. The base URL is used both to resolve relative URLs and when applying
|
||||
* JavaScript's same origin policy. The historyUrl is used for the history
|
||||
* entry.
|
||||
* <p>
|
||||
* The {@code mimeType} parameter specifies the format of the data. If WebView
|
||||
* can't handle the specified MIME type, it will download the data. If
|
||||
* {@code null}, defaults to 'text/html'.
|
||||
* <p>
|
||||
* Note that content specified in this way can access local device files (via
|
||||
* 'file' scheme URLs) only if baseUrl specifies a scheme other than 'http',
|
||||
* 'https', 'ftp', 'ftps', 'about' or 'javascript'.
|
||||
* <p>
|
||||
* If the base URL uses the data scheme, this method is equivalent to calling
|
||||
* {@link #loadData(String,String,String) loadData()} and the historyUrl is
|
||||
* ignored, and the data will be treated as part of a data: URL. If the base URL
|
||||
* uses any other scheme, then the data will be loaded into the WebView as a
|
||||
* plain string (i.e. not part of a data URL) and any URL-encoded entities in
|
||||
* the string will not be decoded.
|
||||
* <p>
|
||||
* Note that the baseUrl is sent in the 'Referer' HTTP header when requesting
|
||||
* subresources (images, etc.) of the page loaded using this method.
|
||||
* <p>
|
||||
* If a valid HTTP or HTTPS base URL is not specified in {@code baseUrl}, then
|
||||
* content loaded using this method will have a {@code window.origin} value of
|
||||
* {@code "null"}. This must not be considered to be a trusted origin by the
|
||||
* application or by any JavaScript code running inside the WebView (for
|
||||
* example, event sources in DOM event handlers or web messages), because
|
||||
* malicious content can also create frames with a null origin. If you need to
|
||||
* identify the main frame's origin in a trustworthy way, you should use a valid
|
||||
* HTTP or HTTPS base URL to set the origin.
|
||||
*
|
||||
* @param baseUrl the URL to use as the page's base URL. If {@code null}
|
||||
* defaults to 'about:blank'.
|
||||
* @param data a String of data in the given encoding
|
||||
* @param mimeType the MIME type of the data, e.g. 'text/html'.
|
||||
* @param encoding the encoding of the data
|
||||
* @param historyUrl the URL to use as the history entry. If {@code null}
|
||||
* defaults to 'about:blank'. If non-null, this must be a
|
||||
* valid URL.
|
||||
*/
|
||||
public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the WebViewClient that will receive various notifications and
|
||||
* requests. This will replace the current handler.
|
||||
*
|
||||
* @param client an implementation of WebViewClient
|
||||
* @see #getWebViewClient
|
||||
*/
|
||||
public void setWebViewClient(WebViewClient client) {
|
||||
}
|
||||
/**
|
||||
* Gets the WebViewClient.
|
||||
*
|
||||
* @return the WebViewClient, or a default client if not yet set
|
||||
* @see #setWebViewClient
|
||||
*/
|
||||
public WebViewClient getWebViewClient() {
|
||||
public String[] getHttpAuthUsernamePassword(String host, String realm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the supplied Java object into this WebView. The object is
|
||||
* injected into the JavaScript context of the main frame, using the
|
||||
* supplied name. This allows the Java object's methods to be
|
||||
* accessed from JavaScript. For applications targeted to API
|
||||
* level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
|
||||
* and above, only public methods that are annotated with
|
||||
* {@link android.webkit.JavascriptInterface} can be accessed from JavaScript.
|
||||
* For applications targeted to API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or below,
|
||||
* all public methods (including the inherited ones) can be accessed, see the
|
||||
* important security note below for implications.
|
||||
* <p> Note that injected objects will not appear in JavaScript until the page is next
|
||||
* (re)loaded. JavaScript should be enabled before injecting the object. For example:
|
||||
* <pre>
|
||||
* class JsObject {
|
||||
* {@literal @}JavascriptInterface
|
||||
* public String toString() { return "injectedObject"; }
|
||||
* }
|
||||
* webview.getSettings().setJavaScriptEnabled(true);
|
||||
* webView.addJavascriptInterface(new JsObject(), "injectedObject");
|
||||
* webView.loadData("<!DOCTYPE html><title></title>", "text/html", null);
|
||||
* webView.loadUrl("javascript:alert(injectedObject.toString())");</pre>
|
||||
* <p>
|
||||
* <strong>IMPORTANT:</strong>
|
||||
* <ul>
|
||||
* <li> This method can be used to allow JavaScript to control the host
|
||||
* application. This is a powerful feature, but also presents a security
|
||||
* risk for apps targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or earlier.
|
||||
* Apps that target a version later than {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
|
||||
* are still vulnerable if the app runs on a device running Android earlier than 4.2.
|
||||
* The most secure way to use this method is to target {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
|
||||
* and to ensure the method is called only when running on Android 4.2 or later.
|
||||
* With these older versions, JavaScript could use reflection to access an
|
||||
* injected object's public fields. Use of this method in a WebView
|
||||
* containing untrusted content could allow an attacker to manipulate the
|
||||
* host application in unintended ways, executing Java code with the
|
||||
* permissions of the host application. Use extreme care when using this
|
||||
* method in a WebView which could contain untrusted content.</li>
|
||||
* <li> JavaScript interacts with Java object on a private, background
|
||||
* thread of this WebView. Care is therefore required to maintain thread
|
||||
* safety.
|
||||
* </li>
|
||||
* <li> The Java object's fields are not accessible.</li>
|
||||
* <li> For applications targeted to API level {@link android.os.Build.VERSION_CODES#LOLLIPOP}
|
||||
* and above, methods of injected Java objects are enumerable from
|
||||
* JavaScript.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param object the Java object to inject into this WebView's JavaScript
|
||||
* context. {@code null} values are ignored.
|
||||
* @param name the name used to expose the object in JavaScript
|
||||
*/
|
||||
public void addJavascriptInterface(Object object, String name) {
|
||||
}
|
||||
/**
|
||||
* Removes a previously injected Java object from this WebView. Note that
|
||||
* the removal will not be reflected in JavaScript until the page is next
|
||||
* (re)loaded. See {@link #addJavascriptInterface}.
|
||||
*
|
||||
* @param name the name used to expose the object in JavaScript
|
||||
*/
|
||||
public void removeJavascriptInterface(String name) {
|
||||
public void destroy() {}
|
||||
|
||||
public static void enablePlatformNotifications() {}
|
||||
|
||||
public static void disablePlatformNotifications() {}
|
||||
|
||||
public void loadUrl(String url) {}
|
||||
|
||||
public void loadData(String data, String mimeType, String encoding) {}
|
||||
|
||||
public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,
|
||||
String failUrl) {}
|
||||
|
||||
public void stopLoading() {}
|
||||
|
||||
public void reload() {}
|
||||
|
||||
public boolean canGoBack() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void goBack() {}
|
||||
|
||||
public boolean canGoForward() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void goForward() {}
|
||||
|
||||
public boolean canGoBackOrForward(int steps) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void goBackOrForward(int steps) {}
|
||||
|
||||
public boolean pageUp(boolean top) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean pageDown(boolean bottom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void clearView() {}
|
||||
|
||||
public float getScale() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setInitialScale(int scaleInPercent) {}
|
||||
|
||||
public void invokeZoomPicker() {}
|
||||
|
||||
public String getUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getProgress() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getContentHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void pauseTimers() {}
|
||||
|
||||
public void resumeTimers() {}
|
||||
|
||||
public void clearCache() {}
|
||||
|
||||
public void clearFormData() {}
|
||||
|
||||
public void clearHistory() {}
|
||||
|
||||
public void clearSslPreferences() {}
|
||||
|
||||
public static String findAddress(String addr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setWebViewClient(WebViewClient client) {}
|
||||
|
||||
public void addJavascriptInterface(Object obj, String interfaceName) {}
|
||||
|
||||
public View getZoomControls() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean zoomIn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean zoomOut() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the WebSettings object used to control the settings for this
|
||||
* WebView.
|
||||
*
|
||||
* @return a WebSettings object that can be used to control this WebView's
|
||||
* settings
|
||||
*/
|
||||
public WebSettings getSettings() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user