diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index 839d1fa69e2..ab7404d1011 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -132,6 +132,8 @@ private module Frameworks { private import semmle.code.java.frameworks.Hibernate private import semmle.code.java.frameworks.jOOQ private import semmle.code.java.frameworks.spring.SpringHttp + private import semmle.code.java.frameworks.android.ContentProviders + private import semmle.code.java.frameworks.android.Widget } private predicate sourceModelCsv(string row) { diff --git a/java/ql/lib/semmle/code/java/frameworks/android/ContentProviders.qll b/java/ql/lib/semmle/code/java/frameworks/android/ContentProviders.qll new file mode 100644 index 00000000000..abda2af3c03 --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/android/ContentProviders.qll @@ -0,0 +1,23 @@ +/** + * Provides classes and predicates for working with Content Providers. + */ + +import java +import semmle.code.java.dataflow.ExternalFlow + +/** The class `android.content.ContentValues`. */ +class ContentValues extends Class { + ContentValues() { this.hasQualifiedName("android.content", "ContentValues") } +} + +private class SummaryModels extends SummaryModelCsv { + override predicate row(string row) { + row = + [ + "android.content;ContentValues;false;put;;;Argument[0];MapKey of Argument[-1];value", + "android.content;ContentValues;false;put;;;Argument[1];MapValue of Argument[-1];value", + "android.content;ContentValues;false;putAll;;;MapKey of Argument[0];MapKey of Argument[-1];value", + "android.content;ContentValues;false;putAll;;;MapValue of Argument[0];MapValue of Argument[-1];value" + ] + } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/android/SQLite.qll b/java/ql/lib/semmle/code/java/frameworks/android/SQLite.qll index 1d189cca569..5c1a9d6e600 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/SQLite.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/SQLite.qll @@ -24,6 +24,14 @@ class TypeDatabaseUtils extends Class { TypeDatabaseUtils() { hasQualifiedName("android.database", "DatabaseUtils") } } +class TypeSQLiteOpenHelper extends Class { + TypeSQLiteOpenHelper() { this.hasQualifiedName("android.database.sqlite", "SQLiteOpenHelper") } +} + +class TypeSQLiteStatement extends Class { + TypeSQLiteStatement() { this.hasQualifiedName("android.database.sqlite", "SQLiteStatement") } +} + private class SQLiteSinkCsv extends SinkModelCsv { override predicate row(string row) { row = diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll b/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll new file mode 100644 index 00000000000..f075e758b31 --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll @@ -0,0 +1,21 @@ +import java +import semmle.code.java.dataflow.ExternalFlow +import semmle.code.java.dataflow.FlowSources + +private class AndroidWidgetSourceModels extends SourceModelCsv { + override predicate row(string row) { + row = ["android.widget;EditText;true;getText;;;ReturnValue;android-widget"] + } +} + +private class DefaultAndroidWidgetSources extends RemoteFlowSource { + DefaultAndroidWidgetSources() { sourceNode(this, "android-widget") } + + override string getSourceType() { result = "Android widget source" } +} + +private class AndroidWidgetSummaryModels extends SummaryModelCsv { + override predicate row(string row) { + row = ["android.widget;EditText;true;getText;;;Argument[-1];ReturnValue;taint"] + } +} diff --git a/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidDatabaseQuery.qll b/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidDatabaseQuery.qll new file mode 100644 index 00000000000..327d26a7ebb --- /dev/null +++ b/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidDatabaseQuery.qll @@ -0,0 +1,119 @@ +/** Provides classes and predicates to reason about cleartext storage in Android databases. */ + +import java +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.frameworks.android.ContentProviders +import semmle.code.java.frameworks.android.Intent +import semmle.code.java.frameworks.android.SQLite +import semmle.code.java.security.CleartextStorageQuery + +private class LocalDatabaseCleartextStorageSink extends CleartextStorageSink { + LocalDatabaseCleartextStorageSink() { localDatabaseInput(_, this.asExpr()) } +} + +private class LocalDatabaseCleartextStorageStep extends CleartextStorageAdditionalTaintStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + // EditText.getText() return type is parsed as `Object`, so we need to + // add a taint step for `Object.toString` to model `editText.getText().toString()` + exists(MethodAccess ma, Method m | + ma.getMethod() = m and + m.getDeclaringType() instanceof TypeObject and + m.hasName("toString") + | + n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma + ) + } +} + +/** The creation of an object that can be used to store data in a local database. */ +class LocalDatabaseOpenMethodAccess extends Storable, Call { + LocalDatabaseOpenMethodAccess() { + exists(Method m | this.(MethodAccess).getMethod() = m | + m.getDeclaringType().getASupertype*() instanceof TypeSQLiteOpenHelper and + m.hasName("getWritableDatabase") + or + m.getDeclaringType() instanceof TypeSQLiteDatabase and + m.hasName(["create", "open%Database", "compileStatement"]) + or + m.getDeclaringType().getASupertype*() instanceof TypeContext and + m.hasName("openOrCreateDatabase") + ) + or + this.(ClassInstanceExpr).getConstructedType() instanceof ContentValues + } + + override Expr getAnInput() { + exists(LocalDatabaseFlowConfig config, DataFlow::Node database | + localDatabaseInput(database, result) and + config.hasFlow(DataFlow::exprNode(this), database) + ) + } + + override Expr getAStore() { + exists(LocalDatabaseFlowConfig config, DataFlow::Node database | + localDatabaseStore(database, result) and + config.hasFlow(DataFlow::exprNode(this), database) + ) + } +} + +/** A method that is both a database input and a database store. */ +private class LocalDatabaseInputStoreMethod extends Method { + LocalDatabaseInputStoreMethod() { + this.getDeclaringType() instanceof TypeSQLiteDatabase and + this.getName().matches("exec%SQL") + } +} + +private predicate localDatabaseInput(DataFlow::Node database, Argument input) { + exists(Method m | input.getCall().(MethodAccess).getMethod() = m | + m instanceof LocalDatabaseInputStoreMethod and + database.asExpr() = input.getCall().getQualifier() + or + m.getDeclaringType() instanceof TypeSQLiteDatabase and + m.hasName("compileStatement") and + database.asExpr() = input.getCall() + or + m.getDeclaringType() instanceof ContentValues and + m.hasName("put") and + input.getPosition() = 1 and + database.asExpr() = input.getCall().getQualifier() + ) +} + +private predicate localDatabaseStore(DataFlow::Node database, MethodAccess store) { + exists(Method m | store.getMethod() = m | + m instanceof LocalDatabaseInputStoreMethod and + database.asExpr() = store.getQualifier() + or + m.getDeclaringType() instanceof TypeSQLiteDatabase and + m.getName().matches(["insert%", "replace%", "update%"]) and + database.asExpr() = store.getAnArgument() and + database.getType() instanceof ContentValues + or + m.getDeclaringType() instanceof TypeSQLiteStatement and + m.hasName(["executeInsert", "executeUpdateDelete"]) and + database.asExpr() = store.getQualifier() + ) +} + +private class LocalDatabaseFlowConfig extends DataFlow::Configuration { + LocalDatabaseFlowConfig() { this = "LocalDatabaseFlowConfig" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof LocalDatabaseOpenMethodAccess + } + + override predicate isSink(DataFlow::Node sink) { + localDatabaseInput(sink, _) or + localDatabaseStore(sink, _) + } + + override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(Field f | + f.getType() instanceof TypeSQLiteDatabase and + f.getAnAssignedValue() = n1.asExpr() and + f = n2.asExpr().(FieldRead).getField() + ) + } +} diff --git a/java/ql/lib/semmle/code/java/security/SensitiveActions.qll b/java/ql/lib/semmle/code/java/security/SensitiveActions.qll index c0c45975d8b..05dcccc36ce 100644 --- a/java/ql/lib/semmle/code/java/security/SensitiveActions.qll +++ b/java/ql/lib/semmle/code/java/security/SensitiveActions.qll @@ -14,13 +14,19 @@ import java private string suspicious() { - result = ["%password%", "%passwd%", "%account%", "%accnt%", "%trusted%"] + result = + [ + "%password%", "%passwd%", "pwd", "%account%", "%accnt%", "%trusted%", "%refresh%token%", + "%secret%token" + ] } private string nonSuspicious() { result = "%hashed%" or result = "%encrypted%" or - result = "%crypt%" + result = "%crypt%" or + result = "%create table%" or + result = "%drop table%" } /** diff --git a/java/ql/src/Security/CWE/CWE-312/CleartextStorageAndroidDatabase.java b/java/ql/src/Security/CWE/CWE-312/CleartextStorageAndroidDatabase.java new file mode 100644 index 00000000000..c421e7f3de5 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-312/CleartextStorageAndroidDatabase.java @@ -0,0 +1,18 @@ +public void sqliteStorageUnsafe(Context ctx, String name, String password) { + // BAD - sensitive information saved in cleartext. + SQLiteDatabase db = ctx.openOrCreateDatabase("test", Context.MODE_PRIVATE, null); + db.execSQL("INSERT INTO users VALUES (?, ?)", new String[] {name, password}); +} + +public void sqliteStorageSafe(Context ctx, String name, String password) { + // GOOD - sensitive information encrypted with a custom method. + SQLiteDatabase db = ctx.openOrCreateDatabase("test", Context.MODE_PRIVATE, null); + db.execSQL("INSERT INTO users VALUES (?, ?)", new String[] {name, encrypt(password)}); +} + +public void sqlCipherStorageSafe(String name, String password, String databasePassword) { + // GOOD - sensitive information saved using SQLCipher. + net.sqlcipher.database.SQLiteDatabase db = + net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase("test", databasePassword, null); + db.execSQL("INSERT INTO users VALUES (?, ?)", new String[] {name, password}); +} diff --git a/java/ql/src/Security/CWE/CWE-312/CleartextStorageAndroidDatabase.qhelp b/java/ql/src/Security/CWE/CWE-312/CleartextStorageAndroidDatabase.qhelp new file mode 100644 index 00000000000..3e190aa6a47 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-312/CleartextStorageAndroidDatabase.qhelp @@ -0,0 +1,36 @@ + + + +

+ SQLite is a lightweight database engine commonly used in Android devices to store data. By itself, SQLite does not offer any encryption mechanism by default and stores all data in plaintext, which introduces a risk if sensitive data like credentials, authentication tokens or personal identifiable information (PII) are directly stored in a SQLite database. The information could be accessed by any process or user in rooted devices, or can be disclosed through chained vulnerabilities, like unexpected access to the private storage through exposed components. +

+
+ + +

+ Use SQLCipher or similar libraries to add encryption capabilities to SQLite. Alternatively, encrypt sensitive data using cryptographicaly secure algorithms before storing it in the database. +

+
+ + +

+ In the first example, sensitive user information is stored in cleartext. +

+ +

+ In the second and third examples, the code encrypts sensitive information before saving it to the database. +

+ +
+ + +
  • + Android Developers: + Work with data more securely +
  • +
  • + SQLCipher: + Android Application Integration +
  • +
    +
    diff --git a/java/ql/src/Security/CWE/CWE-312/CleartextStorageAndroidDatabase.ql b/java/ql/src/Security/CWE/CWE-312/CleartextStorageAndroidDatabase.ql new file mode 100644 index 00000000000..88c1c6f856c --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-312/CleartextStorageAndroidDatabase.ql @@ -0,0 +1,23 @@ +/** + * @name Cleartext storage of sensitive information using a local database on Android + * @description Cleartext Storage of Sensitive Information using + * a local database on Android allows access for users with root + * privileges or unexpected exposure from chained vulnerabilities. + * @kind problem + * @problem.severity warning + * @precision medium + * @id java/android/cleartext-storage-database + * @tags security + * external/cwe/cwe-312 + */ + +import java +import semmle.code.java.security.CleartextStorageAndroidDatabaseQuery + +from SensitiveSource data, LocalDatabaseOpenMethodAccess s, Expr input, Expr store +where + input = s.getAnInput() and + store = s.getAStore() and + data.flowsToCached(input) +select store, "SQLite database $@ containing $@ is stored $@. Data was added $@.", s, s.toString(), + data, "sensitive data", store, "here", input, "here" diff --git a/java/ql/src/change-notes/2021-08-17-cleartext-storage-database-query.md b/java/ql/src/change-notes/2021-08-17-cleartext-storage-database-query.md new file mode 100644 index 00000000000..ce71dca1a5f --- /dev/null +++ b/java/ql/src/change-notes/2021-08-17-cleartext-storage-database-query.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* A new query "Cleartext storage of sensitive information using a local database on Android" (`java/android/cleartext-storage-database`) has been added. This query finds instances of sensitive data being stored in local databases without encryption, which may expose it to attackers or malicious applications. diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.expected b/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.java b/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.java new file mode 100644 index 00000000000..0b4e317534c --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.java @@ -0,0 +1,138 @@ +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.Base64; +import android.app.Activity; +import android.content.ContentValues; +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteStatement; + +public class CleartextStorageAndroidDatabaseTest extends Activity { + + public void testCleartextStorageAndroiDatabaseSafe1(Context ctx, String name, String password) { + SQLiteDatabase db = ctx.openOrCreateDatabase("test", Context.MODE_PRIVATE, null); + db.execSQL("CREATE TABLE IF NOT EXISTS users(user VARCHAR, password VARCHAR);"); // Safe + } + + public void testCleartextStorageAndroiDatabaseSafe2(Context ctx, String name, String password) { + SQLiteDatabase db = ctx.openOrCreateDatabase("test", Context.MODE_PRIVATE, null); + db.execSQL("DROP TABLE passwords;"); // Safe - no sensitive value being stored + } + + public void testCleartextStorageAndroiDatabase1(Context ctx, String name, String password) { + SQLiteDatabase db = ctx.openOrCreateDatabase("test", Context.MODE_PRIVATE, null); + String query = "INSERT INTO users VALUES ('" + name + "', '" + password + "');"; + db.execSQL(query); // $ hasCleartextStorageAndroidDatabase + } + + public void testCleartextStorageAndroiDatabase2(String name, String password) { + SQLiteDatabase db = SQLiteDatabase.create(null); + String query = "INSERT INTO users VALUES (?, ?)"; + db.execSQL(query, new String[] {name, password}); // $ hasCleartextStorageAndroidDatabase + } + + //@formatter:off + public void testCleartextStorageAndroiDatabase3(String name, String password) { + SQLiteDatabase db = SQLiteDatabase.create(null); + String query = "INSERT INTO users VALUES (?, ?)"; + db.execPerConnectionSQL(query, new String[] {name, password}); // $ hasCleartextStorageAndroidDatabase + } + //@formatter:on + + public void testCleartextStorageAndroiDatabaseSafe3(String name, String password) { + SQLiteDatabase db = SQLiteDatabase.openDatabase("", null, 0); + ContentValues cv = new ContentValues(); + cv.put("username", name); + cv.put("password", password); // Safe - ContentValues aren't added to any database + } + + public void testCleartextStorageAndroiDatabase4(String name, String password) { + SQLiteDatabase db = SQLiteDatabase.openDatabase("", null, 0); + ContentValues cv = new ContentValues(); + cv.put("username", name); + cv.put("password", password); // $ hasCleartextStorageAndroidDatabase + db.insert("table", null, cv); + } + + public void testCleartextStorageAndroiDatabase5(String name, String password) { + SQLiteDatabase db = SQLiteDatabase.openDatabase("", null, 0); + ContentValues cv = new ContentValues(); + cv.put("username", name); + cv.put("password", password); // $ hasCleartextStorageAndroidDatabase + db.insertOrThrow("table", null, cv); + } + + public void testCleartextStorageAndroiDatabase6(String name, String password) { + SQLiteDatabase db = SQLiteDatabase.openDatabase("", null, 0); + ContentValues cv = new ContentValues(); + cv.put("username", name); + cv.put("password", password); // $ hasCleartextStorageAndroidDatabase + db.insertWithOnConflict("table", null, cv, 0); + } + + public void testCleartextStorageAndroiDatabase7(String name, String password) { + SQLiteDatabase db = SQLiteDatabase.openDatabase("", null, 0); + ContentValues cv = new ContentValues(); + cv.put("username", name); + cv.put("password", password); // $ hasCleartextStorageAndroidDatabase + db.replace("table", null, cv); + } + + public void testCleartextStorageAndroiDatabase8(String name, String password) { + SQLiteDatabase db = SQLiteDatabase.openDatabase("", null, 0); + ContentValues cv = new ContentValues(); + cv.put("username", name); + cv.put("password", password); // $ hasCleartextStorageAndroidDatabase + db.replaceOrThrow("table", null, cv); + } + + public void testCleartextStorageAndroiDatabase9(String name, String password) { + SQLiteDatabase db = SQLiteDatabase.openDatabase("", null, 0); + ContentValues cv = new ContentValues(); + cv.put("username", name); + cv.put("password", password); // $ hasCleartextStorageAndroidDatabase + db.update("table", cv, "", new String[] {}); + } + + public void testCleartextStorageAndroiDatabase10(String name, String password) { + SQLiteDatabase db = SQLiteDatabase.openDatabase("", null, 0); + ContentValues cv = new ContentValues(); + cv.put("username", name); + cv.put("password", password); // $ hasCleartextStorageAndroidDatabase + db.updateWithOnConflict("table", cv, "", new String[] {}, 0); + } + + public void testCleartextStorageAndroiDatabaseSafe4(SQLiteDatabase db, String name, + String password) { + String query = "INSERT INTO users VALUES ('" + name + "', '" + password + "');"; + SQLiteStatement stmt = db.compileStatement(query); // Safe - statement isn't executed + } + + public void testCleartextStorageAndroiDatabase11(SQLiteDatabase db, String name, + String password) { + String query = "INSERT INTO users VALUES ('" + name + "', '" + password + "');"; + SQLiteStatement stmt = db.compileStatement(query); // $ hasCleartextStorageAndroidDatabase + stmt.executeUpdateDelete(); + } + + public void testCleartextStorageAndroiDatabase12(SQLiteDatabase db, String name, + String password) { + String query = "INSERT INTO users VALUES ('" + name + "', '" + password + "');"; + SQLiteStatement stmt = db.compileStatement(query); // $ hasCleartextStorageAndroidDatabase + stmt.executeInsert(); + } + + public void testCleartextStorageAndroiDatabaseSafe5(String name, String password) + throws Exception { + SQLiteDatabase db = SQLiteDatabase.create(null); + String query = "INSERT INTO users VALUES (?, ?)"; + db.execSQL(query, new String[] {name, encrypt(password)}); // Safe + } + + private static String encrypt(String cleartext) throws Exception { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(cleartext.getBytes(StandardCharsets.UTF_8)); + String encoded = Base64.getEncoder().encodeToString(hash); + return encoded; + } +} diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.ql b/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.ql new file mode 100644 index 00000000000..ea754b1a96e --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.ql @@ -0,0 +1,22 @@ +import java +import semmle.code.java.security.CleartextStorageAndroidDatabaseQuery +import TestUtilities.InlineExpectationsTest + +class CleartextStorageAndroidDatabaseTest extends InlineExpectationsTest { + CleartextStorageAndroidDatabaseTest() { this = "CleartextStorageAndroidDatabaseTest" } + + override string getARelevantTag() { result = "hasCleartextStorageAndroidDatabase" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasCleartextStorageAndroidDatabase" and + exists(SensitiveSource data, LocalDatabaseOpenMethodAccess s, Expr input, Expr store | + input = s.getAnInput() and + store = s.getAStore() and + data.flowsToCached(input) + | + input.getLocation() = location and + element = input.toString() and + value = "" + ) + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/annotation/IntRange.java b/java/ql/test/stubs/google-android-9.0.0/android/annotation/IntRange.java new file mode 100644 index 00000000000..fdd1786ea5e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/annotation/IntRange.java @@ -0,0 +1,21 @@ +/* + * 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; + +public @interface IntRange { + long from() default Long.MIN_VALUE; + + long to() default Long.MAX_VALUE; + +} 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 08865e47a8e..e95df1dcad5 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 @@ -101,6 +101,7 @@ abstract public class Context public abstract String getSystemServiceName(Class p0); public abstract String[] databaseList(); public abstract String[] fileList(); +<<<<<<< HEAD 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); 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 ad2631b3149..4fd200f2556 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 @@ -15,6 +15,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +<<<<<<< HEAD import android.util.AttributeSet; import java.io.Serializable; import java.util.ArrayList; diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/DatabaseUtils.java b/java/ql/test/stubs/google-android-9.0.0/android/database/DatabaseUtils.java new file mode 100644 index 00000000000..0d0414c9fdf --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/DatabaseUtils.java @@ -0,0 +1,46 @@ +package android.database; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.os.ParcelFileDescriptor; + +public class DatabaseUtils { + + public static ParcelFileDescriptor blobFileDescriptorForQuery(SQLiteDatabase db, String query, + String[] selectionArgs) { + return null; + } + + public static long longForQuery(SQLiteDatabase db, String query, String[] selectionArgs) { + return 0; + + } + + public static String stringForQuery(SQLiteDatabase db, String query, String[] selectionArgs) { + return null; + + } + + public static void createDbFromSqlStatements(Context context, String dbName, int dbVersion, String sqlStatements) { + + } + + public static int queryNumEntries(SQLiteDatabase db, String table, String selection) { + return 0; + + } + + public static int queryNumEntries(SQLiteDatabase db, String table, String selection, String[] selectionArgs) { + return 0; + + } + + public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) { + return null; + } + + public static String concatenateWhere(String a, String b) { + return null; + } + +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/SQLException.java b/java/ql/test/stubs/google-android-9.0.0/android/database/SQLException.java new file mode 100644 index 00000000000..87da8071d5e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/SQLException.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006 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.database; + +public class SQLException extends RuntimeException { + public SQLException() { + } + + public SQLException(String error) { + } + + public SQLException(String error, Throwable cause) { + } + +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteDatabase.java b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteDatabase.java index 64b62e68d44..a698c7e952f 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteDatabase.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteDatabase.java @@ -1,3 +1,4 @@ +<<<<<<< HEAD // Generated automatically from android.database.sqlite.SQLiteDatabase for testing purposes package android.database.sqlite; diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteException.java b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteException.java new file mode 100644 index 00000000000..323251bc00b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteException.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2006 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.database.sqlite; +import android.database.SQLException; + +public class SQLiteException extends SQLException { + public SQLiteException() { + } + + public SQLiteException(String error) { + } + + public SQLiteException(String error, Throwable cause) { + } + +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteQueryBuilder.java b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteQueryBuilder.java new file mode 100644 index 00000000000..01b8942c6d7 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteQueryBuilder.java @@ -0,0 +1,57 @@ +package android.database.sqlite; + +import java.util.Map; +import java.util.Set; + +import android.content.ContentValues; +import android.os.CancellationSignal; + +public abstract class SQLiteQueryBuilder { + public abstract void delete(SQLiteDatabase db, String selection, String[] selectionArgs); + + public abstract void insert(SQLiteDatabase db, ContentValues values); + + public abstract void query(SQLiteDatabase db, String[] projectionIn, String selection, String[] selectionArgs, + String groupBy, String having, String sortOrder); + + public abstract void query(SQLiteDatabase db, String[] projectionIn, String selection, String[] selectionArgs, + String groupBy, String having, String sortOrder, String limit); + + public abstract void query(SQLiteDatabase db, String[] projectionIn, String selection, String[] selectionArgs, + String groupBy, String having, String sortOrder, String limit, CancellationSignal cancellationSignal); + + public abstract void update(SQLiteDatabase db, ContentValues values, String selection, String[] selectionArgs); + + public static String buildQueryString(boolean distinct, String tables, String[] columns, String where, + String groupBy, String having, String orderBy, String limit) { + return null; + } + + public abstract String buildQuery(String[] projectionIn, String selection, String groupBy, String having, String sortOrder, + String limit); + + public abstract String buildQuery(String[] projectionIn, String selection, String[] selectionArgs, String groupBy, + String having, String sortOrder, String limit); + + public abstract String buildUnionQuery(String[] subQueries, String sortOrder, String limit); + + public abstract String buildUnionSubQuery(String typeDiscriminatorColumn, String[] unionColumns, + Set columnsPresentInTable, int computedColumnsOffset, String typeDiscriminatorValue, + String selection, String[] selectionArgs, String groupBy, String having); + + public abstract String buildUnionSubQuery(String typeDiscriminatorColumn, String[] unionColumns, + Set columnsPresentInTable, int computedColumnsOffset, String typeDiscriminatorValue, + String selection, String groupBy, String having); + + public static void appendColumns(StringBuilder s, String[] columns) { + } + + public abstract void setProjectionMap(Map columnMap); + + public abstract void setTables(String inTables); + + public abstract void appendWhere(CharSequence inWhere); + + public abstract void appendWhereStandalone(CharSequence inWhere); + +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java index 4beb1cf5dee..190aa81c80d 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java @@ -12,6 +12,7 @@ import android.util.SizeF; import android.util.SparseArray; import java.io.Serializable; import java.util.ArrayList; +<<<<<<< HEAD public class Bundle extends BaseBundle implements Cloneable, Parcelable { diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java index 0df04265046..1a2ff3cc1da 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java @@ -16,7 +16,6 @@ package android.webkit; import java.io.InputStream; -import java.io.StringBufferInputStream; import java.util.Map; /** diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java index e6a7d5d7050..33c9a1b8a57 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java @@ -15,11 +15,8 @@ */ package android.webkit; +import java.net.CookieManager; import android.content.Context; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; /** * Manages settings state for a WebView. When a WebView is first created, it diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java index d9625b70771..3065a9df966 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java @@ -18,6 +18,7 @@ import android.content.Context; import android.view.View; public class WebView extends View { + public WebView(Context context) { super(context); } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java index 4b1bd58498b..03a98480210 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java @@ -15,9 +15,6 @@ */ package android.webkit; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - public class WebViewClient { /** * Give the host application a chance to take over the control when a new url is