1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.providers.contacts.sqlite; 18 19 import android.database.Cursor; 20 import android.database.sqlite.SQLiteDatabase; 21 import android.util.Log; 22 23 import com.android.providers.contacts.AbstractContactsProvider; 24 25 import com.google.common.annotations.VisibleForTesting; 26 27 import java.util.ArrayList; 28 import java.util.List; 29 30 /** 31 * Class to extract table/view/column names from databases. 32 */ 33 @VisibleForTesting 34 public class DatabaseAnalyzer { 35 private static final String TAG = "DatabaseAnalyzer"; 36 37 private static final boolean VERBOSE_LOGGING = AbstractContactsProvider.VERBOSE_LOGGING; 38 DatabaseAnalyzer()39 private DatabaseAnalyzer() { 40 } 41 42 /** 43 * Find and return all table/view names in a db. 44 */ findTablesAndViews(SQLiteDatabase db)45 private static List<String> findTablesAndViews(SQLiteDatabase db) { 46 final List<String> ret = new ArrayList<>(); 47 try (final Cursor c = db.rawQuery( 48 "SELECT name FROM sqlite_master WHERE type in (\"table\", \"view\")", null)) { 49 while (c.moveToNext()) { 50 ret.add(c.getString(0).toLowerCase()); 51 } 52 } 53 return ret; 54 } 55 56 /** 57 * Find all columns in a table/view. 58 */ findColumns(SQLiteDatabase db, String table)59 private static List<String> findColumns(SQLiteDatabase db, String table) { 60 final List<String> ret = new ArrayList<>(); 61 62 // Open the table/view but requests 0 rows. 63 final Cursor c = db.rawQuery("SELECT * FROM " + table + " WHERE 0 LIMIT 0", null); 64 try { 65 // Collect the column names. 66 for (int i = 0; i < c.getColumnCount(); i++) { 67 ret.add(c.getColumnName(i).toLowerCase()); 68 } 69 } finally { 70 c.close(); 71 } 72 return ret; 73 } 74 75 /** 76 * Return all table/view names that clients shouldn't use in their queries -- basically the 77 * result contains all table/view names, except for the names that are column names of any 78 * tables. 79 */ 80 @VisibleForTesting findTableViewsAllowingColumns(SQLiteDatabase db)81 public static List<String> findTableViewsAllowingColumns(SQLiteDatabase db) { 82 final List<String> tables = findTablesAndViews(db); 83 if (VERBOSE_LOGGING) { 84 Log.d(TAG, "Tables and views:"); 85 } 86 final List<String> ret = new ArrayList<>(tables); // Start with the table/view list. 87 for (String name : tables) { 88 if (VERBOSE_LOGGING) { 89 Log.d(TAG, " " + name); 90 } 91 final List<String> columns = findColumns(db, name); 92 if (VERBOSE_LOGGING) { 93 Log.d(TAG, " Columns: " + columns); 94 } 95 for (String c : columns) { 96 if (ret.remove(c)) { 97 Log.d(TAG, "Removing [" + c + "] from disallow list"); 98 } 99 } 100 } 101 return ret; 102 } 103 } 104