diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
index 23a6e0a7003..bd69eecf2c4 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
@@ -58,27 +58,6 @@ class FetchResourceMethodAccess extends MethodAccess {
}
}
-/**
- * Method access to external inputs of `android.content.Intent` object
- */
-class IntentGetExtraMethodAccess extends MethodAccess {
- IntentGetExtraMethodAccess() {
- this.getMethod().getName().regexpMatch("get\\w+Extra") and
- this.getMethod().getDeclaringType() instanceof TypeIntent
- or
- this.getMethod().getName().regexpMatch("get\\w+") and
- this.getQualifier().(MethodAccess).getMethod().hasName("getExtras") and
- this.getQualifier().(MethodAccess).getMethod().getDeclaringType() instanceof TypeIntent
- }
-}
-
-/**
- * Source of fetching URLs from intent extras
- */
-class UntrustedResourceSource extends DataFlow::ExprNode {
- UntrustedResourceSource() { this.asExpr() instanceof IntentGetExtraMethodAccess }
-}
-
/**
* Holds if `ma` loads URL `sink`
*/
@@ -127,7 +106,7 @@ class UrlResourceSink extends DataFlow::ExprNode {
class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration {
FetchUntrustedResourceConfiguration() { this = "FetchUntrustedResourceConfiguration" }
- override predicate isSource(DataFlow::Node source) { source instanceof UntrustedResourceSource }
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
sink instanceof UrlResourceSink and
diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll
index f92aedbc038..d167b62f790 100644
--- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll
+++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll
@@ -16,6 +16,7 @@ import semmle.code.java.frameworks.android.XmlParsing
import semmle.code.java.frameworks.android.WebView
import semmle.code.java.frameworks.JaxWS
import semmle.code.java.frameworks.javase.WebSocket
+import semmle.code.java.frameworks.android.Android
import semmle.code.java.frameworks.android.Intent
import semmle.code.java.frameworks.spring.SpringWeb
import semmle.code.java.frameworks.spring.SpringController
@@ -323,15 +324,26 @@ class ReverseDNSMethod extends Method {
/** Android `Intent` that may have come from a hostile application. */
class AndroidIntentInput extends DataFlow::Node {
+ Type receiverType;
+
AndroidIntentInput() {
exists(MethodAccess ma, AndroidGetIntentMethod m |
ma.getMethod().overrides*(m) and
- this.asExpr() = ma
+ this.asExpr() = ma and
+ receiverType = ma.getReceiverType()
)
or
exists(Method m, AndroidReceiveIntentMethod rI |
m.overrides*(rI) and
- this.asParameter() = m.getParameter(1)
+ this.asParameter() = m.getParameter(1) and
+ receiverType = m.getDeclaringType()
)
}
}
+
+/** Exported Android `Intent` that may have come from a hostile application. */
+class ExportedAndroidIntentInput extends RemoteFlowSource, AndroidIntentInput {
+ ExportedAndroidIntentInput() { receiverType.(ExportableAndroidComponent).isExported() }
+
+ override string getSourceType() { result = "Exported Android intent source" }
+}
diff --git a/java/ql/src/semmle/code/java/frameworks/android/Android.qll b/java/ql/src/semmle/code/java/frameworks/android/Android.qll
index da500afbe6e..df543e4f11f 100644
--- a/java/ql/src/semmle/code/java/frameworks/android/Android.qll
+++ b/java/ql/src/semmle/code/java/frameworks/android/Android.qll
@@ -30,25 +30,42 @@ class AndroidComponent extends Class {
predicate hasIntentFilter() { exists(getAndroidComponentXmlElement().getAnIntentFilterElement()) }
}
+/**
+ * An Android component that can be explicitly or implicitly exported.
+ */
+class ExportableAndroidComponent extends AndroidComponent {
+ /**
+ * Holds if this Android component is configured as `exported` or has intent
+ * filters configured without `exported` explicitly disabled in an
+ * `AndroidManifest.xml` file.
+ */
+ override predicate isExported() {
+ getAndroidComponentXmlElement().isExported()
+ or
+ hasIntentFilter() and
+ not getAndroidComponentXmlElement().isNotExported()
+ }
+}
+
/** An Android activity. */
-class AndroidActivity extends AndroidComponent {
+class AndroidActivity extends ExportableAndroidComponent {
AndroidActivity() { this.getASupertype*().hasQualifiedName("android.app", "Activity") }
}
/** An Android service. */
-class AndroidService extends AndroidComponent {
+class AndroidService extends ExportableAndroidComponent {
AndroidService() { this.getASupertype*().hasQualifiedName("android.app", "Service") }
}
/** An Android broadcast receiver. */
-class AndroidBroadcastReceiver extends AndroidComponent {
+class AndroidBroadcastReceiver extends ExportableAndroidComponent {
AndroidBroadcastReceiver() {
this.getASupertype*().hasQualifiedName("android.content", "BroadcastReceiver")
}
}
/** An Android content provider. */
-class AndroidContentProvider extends AndroidComponent {
+class AndroidContentProvider extends ExportableAndroidComponent {
AndroidContentProvider() {
this.getASupertype*().hasQualifiedName("android.content", "ContentProvider")
}
diff --git a/java/ql/src/semmle/code/java/frameworks/android/Intent.qll b/java/ql/src/semmle/code/java/frameworks/android/Intent.qll
index c4894a5976c..92c49f3101a 100644
--- a/java/ql/src/semmle/code/java/frameworks/android/Intent.qll
+++ b/java/ql/src/semmle/code/java/frameworks/android/Intent.qll
@@ -42,3 +42,13 @@ class IntentGetExtraMethod extends Method, TaintPreservingCallable {
override predicate returnsTaintFrom(int arg) { arg = -1 }
}
+
+/** A getter on `android.os.BaseBundle` or `android.os.Bundle`. */
+class BundleGetterMethod extends Method, TaintPreservingCallable {
+ BundleGetterMethod() {
+ getDeclaringType().hasQualifiedName("android.os", ["BaseBundle", "Bundle"]) and
+ getName().matches("get%")
+ }
+
+ override predicate returnsTaintFrom(int arg) { arg = -1 }
+}
diff --git a/java/ql/src/semmle/code/xml/AndroidManifest.qll b/java/ql/src/semmle/code/xml/AndroidManifest.qll
index 70afabc8d3d..7c6b2f3e569 100644
--- a/java/ql/src/semmle/code/xml/AndroidManifest.qll
+++ b/java/ql/src/semmle/code/xml/AndroidManifest.qll
@@ -137,6 +137,11 @@ class AndroidComponentXmlElement extends XMLElement {
* Holds if the `android:exported` attribute of this component element is `true`.
*/
predicate isExported() { getExportedAttributeValue() = "true" }
+
+ /**
+ * Holds if the `android:exported` attribute of this component element is explicitly set to `false`.
+ */
+ predicate isNotExported() { getExportedAttributeValue() = "false" }
}
/**
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/AndroidManifest.xml b/java/ql/test/experimental/query-tests/security/CWE-749/AndroidManifest.xml
new file mode 100755
index 00000000000..b215e4d3466
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-749/AndroidManifest.xml
@@ -0,0 +1,51 @@
+
If you don't need to send broadcasts across applications, consider using + * this class with {@link android.support.v4.content.LocalBroadcastManager} instead + * of the more general facilities described below. This will give you a much + * more efficient implementation (no cross-process communication needed) and allow + * you to avoid thinking about any security issues related to other applications + * being able to receive or send your broadcasts. + * + *
You can either dynamically register an instance of this class with
+ * {@link Context#registerReceiver Context.registerReceiver()}
+ * or statically publish an implementation through the
+ * {@link android.R.styleable#AndroidManifestReceiver <receiver>}
+ * tag in your AndroidManifest.xml.
+ *
+ *
Note: + * If registering a receiver in your + * {@link android.app.Activity#onResume() Activity.onResume()} + * implementation, you should unregister it in + * {@link android.app.Activity#onPause() Activity.onPause()}. + * (You won't receive intents when paused, + * and this will cut down on unnecessary system overhead). Do not unregister in + * {@link android.app.Activity#onSaveInstanceState(android.os.Bundle) Activity.onSaveInstanceState()}, + * because this won't be called if the user moves back in the history + * stack. + * + *
There are two major classes of broadcasts that can be received:
+ *Even in the case of normal broadcasts, the system may in some + * situations revert to delivering the broadcast one receiver at a time. In + * particular, for receivers that may require the creation of a process, only + * one will be run at a time to avoid overloading the system with new processes. + * In this situation, however, the non-ordered semantics hold: these receivers still + * cannot return results or abort their broadcast.
+ * + *Note that, although the Intent class is used for sending and receiving + * these broadcasts, the Intent broadcast mechanism here is completely separate + * from Intents that are used to start Activities with + * {@link Context#startActivity Context.startActivity()}. + * There is no way for a BroadcastReceiver + * to see or capture Intents used with startActivity(); likewise, when + * you broadcast an Intent, you will never find or start an Activity. + * These two operations are semantically very different: starting an + * Activity with an Intent is a foreground operation that modifies what the + * user is currently interacting with; broadcasting an Intent is a background + * operation that the user is not normally aware of. + * + *
The BroadcastReceiver class (when launched as a component through + * a manifest's {@link android.R.styleable#AndroidManifestReceiver <receiver>} + * tag) is an important part of an + * application's overall lifecycle.
+ * + *Topics covered here: + *
For information about how to use this class to receive and resolve intents, read the + * Intents and Intent Filters + * developer guide.
+ *Receivers used with the {@link Context} APIs are by their nature a + * cross-application facility, so you must consider how other applications + * may be able to abuse your use of them. Some things to consider are: + * + *
The Intent namespace is global. Make sure that Intent action names and + * other strings are written in a namespace you own, or else you may inadvertantly + * conflict with other applications. + *
When you use {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)}, + * any application may send broadcasts to that registered receiver. You can + * control who can send broadcasts to it through permissions described below. + *
When you publish a receiver in your application's manifest and specify
+ * intent-filters for it, any other application can send broadcasts to it regardless
+ * of the filters you specify. To prevent others from sending to it, make it
+ * unavailable to them with android:exported="false".
+ *
When you use {@link Context#sendBroadcast(Intent)} or related methods, + * normally any other application can receive these broadcasts. You can control who + * can receive such broadcasts through permissions described below. Alternatively, + * starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, you + * can also safely restrict the broadcast to a single application with + * {@link Intent#setPackage(String) Intent.setPackage} + *
None of these issues exist when using + * {@link android.support.v4.content.LocalBroadcastManager}, since intents + * broadcast it never go outside of the current process. + * + *
Access permissions can be enforced by either the sender or receiver + * of a broadcast. + * + *
To enforce a permission when sending, you supply a non-null
+ * permission argument to
+ * {@link Context#sendBroadcast(Intent, String)} or
+ * {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}.
+ * Only receivers who have been granted this permission
+ * (by requesting it with the
+ * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>}
+ * tag in their AndroidManifest.xml) will be able to receive
+ * the broadcast.
+ *
+ *
To enforce a permission when receiving, you supply a non-null
+ * permission when registering your receiver -- either when calling
+ * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)}
+ * or in the static
+ * {@link android.R.styleable#AndroidManifestReceiver <receiver>}
+ * tag in your AndroidManifest.xml. Only broadcasters who have
+ * been granted this permission (by requesting it with the
+ * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>}
+ * tag in their AndroidManifest.xml) will be able to send an
+ * Intent to the receiver.
+ *
+ *
See the Security and Permissions + * document for more information on permissions and security in general. + * + * + *
A BroadcastReceiver object is only valid for the duration of the call + * to {@link #onReceive}. Once your code returns from this function, + * the system considers the object to be finished and no longer active. + * + *
This has important repercussions to what you can do in an + * {@link #onReceive} implementation: anything that requires asynchronous + * operation is not available, because you will need to return from the + * function to handle the asynchronous operation, but at that point the + * BroadcastReceiver is no longer active and thus the system is free to kill + * its process before the asynchronous operation completes. + * + *
In particular, you may not show a dialog or bind to a service from + * within a BroadcastReceiver. For the former, you should instead use the + * {@link android.app.NotificationManager} API. For the latter, you can + * use {@link android.content.Context#startService Context.startService()} to + * send a command to the service. + * + * + *
A process that is currently executing a BroadcastReceiver (that is, + * currently running the code in its {@link #onReceive} method) is + * considered to be a foreground process and will be kept running by the + * system except under cases of extreme memory pressure. + * + *
Once you return from onReceive(), the BroadcastReceiver is no longer + * active, and its hosting process is only as important as any other application + * components that are running in it. This is especially important because if + * that process was only hosting the BroadcastReceiver (a common case for + * applications that the user has never or not recently interacted with), then + * upon returning from onReceive() the system will consider its process + * to be empty and aggressively kill it so that resources are available for other + * more important processes. + * + *
This means that for longer-running operations you will often use + * a {@link android.app.Service} in conjunction with a BroadcastReceiver to keep + * the containing process active for the entire time of your operation. + */ +public abstract class BroadcastReceiver { + + /** + * State for a result that is pending for a broadcast receiver. Returned + * by {@link BroadcastReceiver#goAsync() goAsync()} + * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}. + * This allows you to return from onReceive() without having the broadcast + * terminate; you must call {@link #finish()} once you are done with the + * broadcast. This allows you to process the broadcast off of the main + * thread of your app. + * + *
Note on threading: the state inside of this class is not itself + * thread-safe, however you can use it from any thread if you properly + * sure that you do not have races. Typically this means you will hand + * the entire object to another thread, which will be solely responsible + * for setting any results and finally calling {@link #finish()}. + */ + + public BroadcastReceiver() { + } + /** + * This method is called when the BroadcastReceiver is receiving an Intent + * broadcast. During this time you can use the other methods on + * BroadcastReceiver to view/modify the current result values. This method + * is always called within the main thread of its process, unless you + * explicitly asked for it to be scheduled on a different thread using + * {@link android.content.Context#registerReceiver(BroadcastReceiver, + * IntentFilter, String, android.os.Handler)}. When it runs on the main + * thread you should + * never perform long-running operations in it (there is a timeout of + * 10 seconds that the system allows before considering the receiver to + * be blocked and a candidate to be killed). You cannot launch a popup dialog + * in your implementation of onReceive(). + * + *
If this BroadcastReceiver was launched through a <receiver> tag, + * then the object is no longer alive after returning from this + * function. This means you should not perform any operations that + * return a result to you asynchronously -- in particular, for interacting + * with services, you should use + * {@link Context#startService(Intent)} instead of + * {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish + * to interact with a service that is already running, you can use + * {@link #peekService}. + * + *
The Intent filters used in {@link android.content.Context#registerReceiver} + * and in application manifests are not guaranteed to be exclusive. They + * are hints to the operating system about how to find suitable recipients. It is + * possible for senders to force delivery to specific recipients, bypassing filter + * resolution. For this reason, {@link #onReceive(Context, Intent) onReceive()} + * implementations should respond only to known actions, ignoring any unexpected + * Intents that they may receive. + * + * @param context The Context in which the receiver is running. + * @param intent The Intent being received. + */ + public abstract void onReceive(Context context, Intent intent); +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java index 8c528a58e41..e1ce2140a06 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java @@ -813,4 +813,51 @@ public abstract class Context { * @hide */ public abstract void sendBroadcast(Intent intent, String receiverPermission, int appOp); + + /** + * Broadcast the given intent to all interested BroadcastReceivers, allowing + * an array of required permissions to be enforced. This call is asynchronous; it returns + * immediately, and you will continue executing while the receivers are run. No results are + * propagated from receivers and receivers can not abort the broadcast. If you want to allow + * receivers to propagate results or abort the broadcast, you must send an ordered broadcast + * using {@link #sendOrderedBroadcast(Intent, String)}. + * + *
See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermissions Array of names of permissions that a receiver must hold + * in order to receive your broadcast. + * If empty, no permissions are required. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + * @hide + */ + public abstract void sendBroadcastWithMultiplePermissions (Intent intent, String[] receiverPermissions); + + /** + * Broadcast the given intent to all interested BroadcastReceivers, delivering + * them one at a time to allow more preferred receivers to consume the + * broadcast before it is delivered to less preferred receivers. This + * call is asynchronous; it returns immediately, and you will continue + * executing while the receivers are run. + * + *
See {@link BroadcastReceiver} for more information on Intent broadcasts.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast.
+ * @param receiverPermission (optional) String naming a permissions that
+ * a receiver must hold in order to receive your broadcast.
+ * If null, no permission is required.
+ *
+ * @see android.content.BroadcastReceiver
+ * @see #registerReceiver
+ * @see #sendBroadcast(Intent)
+ * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+ */
+ public abstract void sendOrderedBroadcast(Intent intent, String receiverPermission);
}
\ No newline at end of file
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java
index e3ef6277dba..292069c0f71 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java
@@ -1996,4 +1996,77 @@ public class Intent implements Parcelable, Cloneable {
public void readFromParcel(Parcel in) {
}
+
+ /**
+ * Retrieve the application package name this Intent is limited to. When
+ * resolving an Intent, if non-null this limits the resolution to only
+ * components in the given application package.
+ *
+ * @return The name of the application package for the Intent.
+ *
+ * @see #resolveActivity
+ * @see #setPackage
+ */
+ public String getPackage() {
+ return null;
+ }
+
+ /**
+ * (Usually optional) Set an explicit application package name that limits
+ * the components this Intent will resolve to. If left to the default
+ * value of null, all components in all applications will considered.
+ * If non-null, the Intent can only match the components in the given
+ * application package.
+ *
+ * @param packageName The name of the application package to handle the
+ * intent, or null to allow any application package.
+ *
+ * @return Returns the same Intent object, for chaining multiple calls
+ * into a single statement.
+ *
+ * @see #getPackage
+ * @see #resolveActivity
+ */
+ public Intent setPackage(String packageName) {
+ return null;
+ }
+
+ /**
+ * Convenience for calling {@link #setComponent} with an
+ * explicit class name.
+ *
+ * @param packageContext A Context of the application package implementing
+ * this class.
+ * @param className The name of a class inside of the application package
+ * that will be used as the component for this Intent.
+ *
+ * @return Returns the same Intent object, for chaining multiple calls
+ * into a single statement.
+ *
+ * @see #setComponent
+ * @see #setClass
+ */
+ public Intent setClassName(Context packageContext, String className) {
+ return null;
+ }
+
+ /**
+ * Convenience for calling {@link #setComponent} with an
+ * explicit application package name and class name.
+ *
+ * @param packageName The name of the package implementing the desired
+ * component.
+ * @param className The name of a class inside of the application package
+ * that will be used as the component for this Intent.
+ *
+ * @return Returns the same Intent object, for chaining multiple calls
+ * into a single statement.
+ *
+ * @see #setComponent
+ * @see #setClass
+ */
+ public Intent setClassName(String packageName, String className) {
+ return null;
+ }
+
}
\ No newline at end of file
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java
index 617ec97ae8c..4b85ae8e67a 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java
@@ -129,6 +129,160 @@ public class BaseBundle {
return false;
}
+ /**
+ * Returns true if the given key is contained in the mapping
+ * of this Bundle.
+ *
+ * @param key a String key
+ * @return true if the key is part of the mapping, false otherwise
+ */
+ public boolean containsKey(String key) {
+ return false;
+ }
+
+ /**
+ * Returns the entry with the given key as an object.
+ *
+ * @param key a String key
+ * @return an Object, or null
+ */
+ public Object get(String key) {
+ return null;
+ }
+
+ /**
+ * Removes any entry with the given key from the mapping of this Bundle.
+ *
+ * @param key a String key
+ */
+ public void remove(String key) {
+ }
+
+ /** {@hide} */
+ public void putObject(String key, Object value) {
+ }
+
+ /**
+ * Inserts a Boolean value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a boolean
+ */
+ public void putBoolean(String key, boolean value) {
+ }
+
+ /**
+ * Inserts a byte value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a byte
+ */
+ void putByte(String key, byte value) {
+ }
+
+ /**
+ * Inserts a char value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a char
+ */
+ void putChar(String key, char value) {
+ }
+
+ /**
+ * Inserts a short value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a short
+ */
+ void putShort(String key, short value) {
+ }
+
+ /**
+ * Inserts an int value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value an int
+ */
+ public void putInt(String key, int value) {
+ }
+
+ /**
+ * Inserts a long value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a long
+ */
+ public void putLong(String key, long value) {
+ }
+
+ /**
+ * Inserts a float value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a float
+ */
+ void putFloat(String key, float value) {
+ }
+
+ /**
+ * Inserts a double value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a double
+ */
+ public void putDouble(String key, double value) {
+ }
+
+ /**
+ * Inserts a String value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a String, or null
+ */
+ public void putString(String key, String value) {
+ }
+
+ /**
+ * Inserts a CharSequence value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a CharSequence, or null
+ */
+ void putCharSequence(String key, CharSequence value) {
+ }
+
+ /**
+ * Inserts an ArrayList