1 /* 2 * Copyright (C) 2023 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.server.deviceconfig.db; 18 19 import android.content.ContentValues; 20 import android.database.Cursor; 21 import android.database.sqlite.SQLiteDatabase; 22 import android.text.TextUtils; 23 24 import com.android.server.deviceconfig.db.DeviceConfigDbHelper.Contract.DeviceConfigEntry; 25 26 import java.util.HashMap; 27 import java.util.Map; 28 29 /** 30 * @hide 31 */ 32 public class DeviceConfigDbAdapter { 33 34 private final SQLiteDatabase mDb; 35 DeviceConfigDbAdapter(SQLiteDatabase db)36 public DeviceConfigDbAdapter(SQLiteDatabase db) { 37 mDb = db; 38 } 39 getValuesForNamespace(String namespace, String... keys)40 public Map<String, String> getValuesForNamespace(String namespace, String... keys) { 41 42 String[] projection = { 43 DeviceConfigEntry.COLUMN_NAME_KEY, 44 DeviceConfigEntry.COLUMN_NAME_VALUE 45 }; 46 47 String selection; 48 String[] selectionArgs; 49 if (keys != null && keys.length > 0) { 50 selection = DeviceConfigEntry.COLUMN_NAME_NAMESPACE + " = ? " 51 + "and " + DeviceConfigEntry.COLUMN_NAME_KEY + " in ( ? ) "; 52 String keySelection = TextUtils.join(",", keys); 53 selectionArgs = new String[]{namespace, keySelection}; 54 } else { 55 selection = DeviceConfigEntry.COLUMN_NAME_NAMESPACE + " = ?"; 56 selectionArgs = new String[]{namespace}; 57 } 58 Cursor cursor = mDb.query( 59 DeviceConfigEntry.TABLE_NAME, 60 projection, 61 selection, 62 selectionArgs, 63 null, 64 null, 65 null 66 ); 67 68 Map<String, String> map = new HashMap<>(cursor.getCount()); 69 while (cursor.moveToNext()) { 70 String key = cursor.getString( 71 cursor.getColumnIndexOrThrow(DeviceConfigEntry.COLUMN_NAME_KEY)); 72 String value = cursor.getString( 73 cursor.getColumnIndexOrThrow(DeviceConfigEntry.COLUMN_NAME_VALUE)); 74 map.put(key, value); 75 } 76 cursor.close(); 77 return map; 78 } 79 80 /** 81 * 82 * @return true if the data was inserted or updated in the database 83 */ insertOrUpdateValue_inTransaction(String namespace, String key, String value)84 private boolean insertOrUpdateValue_inTransaction(String namespace, String key, String value) { 85 // TODO(b/265948914): see if this is the most performant way to either insert or update a record 86 ContentValues values = new ContentValues(); 87 values.put(DeviceConfigEntry.COLUMN_NAME_NAMESPACE, namespace); 88 values.put(DeviceConfigEntry.COLUMN_NAME_KEY, key); 89 values.put(DeviceConfigEntry.COLUMN_NAME_VALUE, value); 90 91 String where = DeviceConfigEntry.COLUMN_NAME_NAMESPACE + " = ? " 92 + "and " + DeviceConfigEntry.COLUMN_NAME_VALUE + " = ? "; 93 94 String[] whereArgs = {namespace, key}; 95 int updatedRows = mDb.update(DeviceConfigEntry.TABLE_NAME, values, where, whereArgs); 96 if (updatedRows == 0) { 97 // this is a new row, we need to insert it 98 long id = mDb.insert(DeviceConfigEntry.TABLE_NAME, null, values); 99 return id != -1; 100 } 101 return updatedRows > 0; 102 } 103 104 /** 105 * Set or update the values in the map into the namespace. 106 * 107 * @return true if all values were set. Returns true if the map is empty. 108 */ setValues(String namespace, Map<String, String> map)109 public boolean setValues(String namespace, Map<String, String> map) { 110 if (map.size() == 0) { 111 return true; 112 } 113 boolean allSucceeded = true; 114 try { 115 mDb.beginTransaction(); 116 for (Map.Entry<String, String> entry : map.entrySet()) { 117 // TODO(b/265948914) probably should call yieldIfContendedSafely in this loop 118 allSucceeded &= insertOrUpdateValue_inTransaction(namespace, entry.getKey(), 119 entry.getValue()); 120 } 121 mDb.setTransactionSuccessful(); 122 } finally { 123 mDb.endTransaction(); 124 } 125 return allSucceeded; 126 } 127 128 /** 129 * 130 * @return true if the value was set 131 */ setValue(String namespace, String key, String value, boolean makeDefault)132 public boolean setValue(String namespace, String key, String value, boolean makeDefault) { 133 HashMap<String, String> map = new HashMap<>(); 134 map.put(key, value); 135 return setValues(namespace, map); 136 // TODO(b/265948914) implement make default! 137 } 138 139 /** 140 * 141 * @return true if any value was deleted 142 */ deleteValue(String namespace, String key)143 public boolean deleteValue(String namespace, String key) { 144 String where = DeviceConfigEntry.COLUMN_NAME_NAMESPACE + " = ? " 145 + "and " + DeviceConfigEntry.COLUMN_NAME_KEY + " = ? "; 146 String[] whereArgs = { namespace, key }; 147 int count = mDb.delete(DeviceConfigEntry.TABLE_NAME, where, whereArgs); 148 return count > 0; 149 } 150 } 151