1 /* 2 * Copyright (C) 2022 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.adservices.data.topics; 18 19 import com.android.internal.annotations.VisibleForTesting; 20 21 import java.util.Arrays; 22 import java.util.Collections; 23 import java.util.List; 24 25 /** Container class for Topics API table definitions and constants. */ 26 public final class TopicsTables { 27 28 static final String TOPICS_TABLE_PREFIX = "topics_"; 29 30 /** Topics Taxonomy Table. */ 31 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 32 public interface TaxonomyContract { 33 String TABLE = TOPICS_TABLE_PREFIX + "taxonomy"; 34 String ID = "_id"; 35 String TAXONOMY_VERSION = "taxonomy_version"; 36 String MODEL_VERSION = "model_version"; 37 String TOPIC = "topic"; 38 } 39 40 /** Table Create Statement for the Topics Epoch table */ 41 @VisibleForTesting 42 public static final String CREATE_TABLE_TOPICS_TAXONOMY = 43 "CREATE TABLE " 44 + TaxonomyContract.TABLE 45 + "(" 46 + TaxonomyContract.ID 47 + " INTEGER PRIMARY KEY, " 48 + TaxonomyContract.TAXONOMY_VERSION 49 + " INTEGER NOT NULL, " 50 + TaxonomyContract.MODEL_VERSION 51 + " INTEGER NOT NULL, " 52 + TaxonomyContract.TOPIC 53 + " INTEGER NOT NULL" 54 + ")"; 55 56 /** 57 * This table has apps' classification Topics generated by the ML Classifier. In each epoch 58 * computation, the ML Classifier will generate topics for each app that uses the Topics API in 59 * the epoch. 60 */ 61 public interface AppClassificationTopicsContract { 62 String TABLE = TOPICS_TABLE_PREFIX + "app_classification_topics"; 63 String ID = "_id"; 64 String EPOCH_ID = "epoch_id"; 65 String APP = "app"; 66 String TAXONOMY_VERSION = "taxonomy_version"; 67 String MODEL_VERSION = "model_version"; 68 String TOPIC = "topic"; 69 } 70 71 /** Create Statement for the returned Topics table */ 72 @VisibleForTesting 73 public static final String CREATE_TABLE_APP_CLASSIFICATION_TOPICS = 74 "CREATE TABLE " 75 + AppClassificationTopicsContract.TABLE 76 + "(" 77 + AppClassificationTopicsContract.ID 78 + " INTEGER PRIMARY KEY, " 79 + AppClassificationTopicsContract.EPOCH_ID 80 + " INTEGER NOT NULL, " 81 + AppClassificationTopicsContract.APP 82 + " TEXT NOT NULL, " 83 + AppClassificationTopicsContract.TAXONOMY_VERSION 84 + " INTEGER NOT NULL, " 85 + AppClassificationTopicsContract.MODEL_VERSION 86 + " INTEGER NOT NULL, " 87 + AppClassificationTopicsContract.TOPIC 88 + " INTEGER NOT NULL" 89 + ")"; 90 91 /** 92 * This table has callers and which topics they can learn. Caller can be either (1) app in case 93 * the app called the Topics API directly. (2) sdk in case the sdk called the Topics API. 94 */ 95 public interface CallerCanLearnTopicsContract { 96 String TABLE = TOPICS_TABLE_PREFIX + "caller_can_learn_topic"; 97 String ID = "_id"; 98 String EPOCH_ID = "epoch_id"; 99 String CALLER = "caller"; 100 String TOPIC = "topic"; 101 String TAXONOMY_VERSION = "taxonomy_version"; 102 String MODEL_VERSION = "model_version"; 103 } 104 105 /** Create Statement for the Caller Learned Topic table. */ 106 @VisibleForTesting 107 public static final String CREATE_TABLE_CALLER_CAN_LEARN_TOPICS = 108 "CREATE TABLE " 109 + CallerCanLearnTopicsContract.TABLE 110 + "(" 111 + CallerCanLearnTopicsContract.ID 112 + " INTEGER PRIMARY KEY, " 113 + CallerCanLearnTopicsContract.EPOCH_ID 114 + " INTEGER NOT NULL, " 115 + CallerCanLearnTopicsContract.CALLER 116 + " TEXT NOT NULL, " 117 + CallerCanLearnTopicsContract.TOPIC 118 + " INTEGER NOT NULL, " 119 + CallerCanLearnTopicsContract.TAXONOMY_VERSION 120 + " INTEGER NOT NULL, " 121 + CallerCanLearnTopicsContract.MODEL_VERSION 122 + " INTEGER NOT NULL" 123 + ")"; 124 125 // TODO(b/223446202): Make this table to configurable numbers of top topics. 126 127 /** 128 * Top Topics Table. There are top 5 topics and 1 random topic. In case there is not enough 129 * usage to generate top 5 topics, random ones will be generated. 130 */ 131 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 132 public interface TopTopicsContract { 133 String TABLE = TOPICS_TABLE_PREFIX + "top_topics"; 134 String ID = "_id"; 135 String EPOCH_ID = "epoch_id"; 136 String TOPIC1 = "topic1"; 137 String TOPIC2 = "topic2"; 138 String TOPIC3 = "topic3"; 139 String TOPIC4 = "topic4"; 140 String TOPIC5 = "topic5"; 141 String RANDOM_TOPIC = "random_topic"; 142 String TAXONOMY_VERSION = "taxonomy_version"; 143 String MODEL_VERSION = "model_version"; 144 } 145 146 /** Table Create Statement for the Top Topics table */ 147 @VisibleForTesting 148 public static final String CREATE_TABLE_TOP_TOPICS = 149 "CREATE TABLE " 150 + TopTopicsContract.TABLE 151 + "(" 152 + TopTopicsContract.ID 153 + " INTEGER PRIMARY KEY, " 154 + TopTopicsContract.EPOCH_ID 155 + " INTEGER NOT NULL, " 156 + TopTopicsContract.TOPIC1 157 + " INTEGER NOT NULL, " 158 + TopTopicsContract.TOPIC2 159 + " INTEGER NOT NULL, " 160 + TopTopicsContract.TOPIC3 161 + " INTEGER NOT NULL, " 162 + TopTopicsContract.TOPIC4 163 + " INTEGER NOT NULL, " 164 + TopTopicsContract.TOPIC5 165 + " INTEGER NOT NULL, " 166 + TopTopicsContract.RANDOM_TOPIC 167 + " INTEGER NOT NULL, " 168 + TopTopicsContract.TAXONOMY_VERSION 169 + " INTEGER NOT NULL, " 170 + TopTopicsContract.MODEL_VERSION 171 + " INTEGER NOT NULL" 172 + ")"; 173 174 /** 175 * The returned topic for the app or for the sdk. Note: for App usages directly without any SDK, 176 * the SDK Name is set to empty string. 177 */ 178 public interface ReturnedTopicContract { 179 String TABLE = TOPICS_TABLE_PREFIX + "returned_topics"; 180 String ID = "_id"; 181 String EPOCH_ID = "epoch_id"; 182 String APP = "app"; 183 String SDK = "sdk"; 184 String TAXONOMY_VERSION = "taxonomy_version"; 185 String MODEL_VERSION = "model_version"; 186 String TOPIC = "topic"; 187 String LOGGED_TOPIC = "logged_topic"; 188 } 189 190 /** The returned encrypted topic for the app or for the sdk. */ 191 public interface ReturnedEncryptedTopicContract { 192 String TABLE = TOPICS_TABLE_PREFIX + "returned_encrypted_topics"; 193 String ID = "_id"; 194 String EPOCH_ID = "epoch_id"; 195 String APP = "app"; 196 String SDK = "sdk"; 197 String ENCRYPTED_TOPIC = "encrypted_topic"; 198 String ENCAPSULATED_KEY = "encapsulated_key"; 199 String KEY_IDENTIFIER = "key_identifier"; 200 } 201 202 /** Create Statement for the returned Topics table */ 203 @VisibleForTesting 204 public static final String CREATE_TABLE_RETURNED_TOPIC = 205 "CREATE TABLE " 206 + ReturnedTopicContract.TABLE 207 + "(" 208 + ReturnedTopicContract.ID 209 + " INTEGER PRIMARY KEY, " 210 + ReturnedTopicContract.EPOCH_ID 211 + " INTEGER NOT NULL, " 212 + ReturnedTopicContract.APP 213 + " TEXT NOT NULL, " 214 + ReturnedTopicContract.SDK 215 + " TEXT NOT NULL, " 216 + ReturnedTopicContract.TAXONOMY_VERSION 217 + " INTEGER NOT NULL, " 218 + ReturnedTopicContract.MODEL_VERSION 219 + " INTEGER NOT NULL, " 220 + ReturnedTopicContract.TOPIC 221 + " INTEGER NOT NULL, " 222 + ReturnedTopicContract.LOGGED_TOPIC 223 + " INTEGER" 224 + ")"; 225 226 /** Create Statement for the returned Encrypted Topics table */ 227 public static final String CREATE_TABLE_RETURNED_ENCRYPTED_TOPIC = 228 "CREATE TABLE " 229 + ReturnedEncryptedTopicContract.TABLE 230 + "(" 231 + ReturnedEncryptedTopicContract.ID 232 + " INTEGER PRIMARY KEY, " 233 + ReturnedEncryptedTopicContract.EPOCH_ID 234 + " INTEGER NOT NULL, " 235 + ReturnedEncryptedTopicContract.APP 236 + " TEXT NOT NULL, " 237 + ReturnedEncryptedTopicContract.SDK 238 + " TEXT NOT NULL, " 239 + ReturnedEncryptedTopicContract.ENCRYPTED_TOPIC 240 + " BLOB NOT NULL, " 241 + ReturnedEncryptedTopicContract.KEY_IDENTIFIER 242 + " TEXT NOT NULL, " 243 + ReturnedEncryptedTopicContract.ENCAPSULATED_KEY 244 + " BLOB NOT NULL" 245 + ")"; 246 247 /** 248 * Table to store the app/sdk usage history. Whenever an app or sdk calls the Topics API, one 249 * entry will be generated with the timestamp. 250 */ 251 public interface UsageHistoryContract { 252 String TABLE = TOPICS_TABLE_PREFIX + "usage_history"; 253 String EPOCH_ID = "epoch_id"; 254 String APP = "app"; 255 String SDK = "sdk"; 256 } 257 258 /** Create Statement for the Usage History table */ 259 @VisibleForTesting 260 public static final String CREATE_TABLE_USAGE_HISTORY = 261 "CREATE TABLE " 262 + UsageHistoryContract.TABLE 263 + "(" 264 + UsageHistoryContract.EPOCH_ID 265 + " INTEGER NOT NULL, " 266 + UsageHistoryContract.APP 267 + " TEXT NOT NULL, " 268 + UsageHistoryContract.SDK 269 + " TEXT" 270 + ")"; 271 272 /** 273 * Table to store history for app only Whenever an app calls the Topics API, one entry will be 274 * generated. 275 */ 276 public interface AppUsageHistoryContract { 277 String TABLE = TOPICS_TABLE_PREFIX + "app_usage_history"; 278 String ID = "_id"; 279 String EPOCH_ID = "epoch_id"; 280 String APP = "app"; 281 } 282 283 /** Create Statement for the Usage History App Only table */ 284 @VisibleForTesting 285 public static final String CREATE_TABLE_APP_USAGE_HISTORY = 286 "CREATE TABLE " 287 + AppUsageHistoryContract.TABLE 288 + "(" 289 + AppUsageHistoryContract.ID 290 + " INTEGER PRIMARY KEY, " 291 + AppUsageHistoryContract.EPOCH_ID 292 + " INTEGER NOT NULL, " 293 + AppUsageHistoryContract.APP 294 + " TEXT NOT NULL" 295 + ")"; 296 297 /** Table to store all blocked {@link Topic}s. Blocked topics are controlled by user. */ 298 public interface BlockedTopicsContract { 299 String TABLE = TOPICS_TABLE_PREFIX + "blocked"; 300 String ID = "_id"; 301 String TAXONOMY_VERSION = "taxonomy_version"; 302 String MODEL_VERSION = "model_version"; 303 String TOPIC = "topic"; 304 } 305 306 /** Create Statement for the blocked topics table. */ 307 @VisibleForTesting 308 public static final String CREATE_TABLE_BLOCKED_TOPICS = 309 "CREATE TABLE " 310 + BlockedTopicsContract.TABLE 311 + "(" 312 + BlockedTopicsContract.ID 313 + " INTEGER PRIMARY KEY, " 314 + BlockedTopicsContract.TAXONOMY_VERSION 315 + " INTEGER NOT NULL, " 316 + BlockedTopicsContract.MODEL_VERSION 317 + " INTEGER NOT NULL, " 318 + BlockedTopicsContract.TOPIC 319 + " INTEGER NOT NULL" 320 + ")"; 321 322 /** 323 * Table to store the original timestamp when the user calls Topics API. This table should have 324 * only 1 row that stores the origin. 325 */ 326 public interface EpochOriginContract { 327 String TABLE = TOPICS_TABLE_PREFIX + "epoch_origin"; 328 String ONE_ROW_CHECK = "one_row_check"; // to constrain 1 origin 329 String ORIGIN = "origin"; 330 } 331 332 /** 333 * At the first time inserting a record, it won't persist one_row_check field so that this first 334 * entry will have one_row_check = 1. Therefore, further persisting is not allowed as primary 335 * key cannot be duplicated value and one_row_check is constrained to only equal to 1 to forbid 336 * any increment. 337 */ 338 @VisibleForTesting 339 public static final String CREATE_TABLE_EPOCH_ORIGIN = 340 "CREATE TABLE " 341 + EpochOriginContract.TABLE 342 + "(" 343 + EpochOriginContract.ONE_ROW_CHECK 344 + " INTEGER PRIMARY KEY DEFAULT 1, " 345 + EpochOriginContract.ORIGIN 346 + " INTEGER NOT NULL, " 347 + "CONSTRAINT one_row_constraint CHECK (" 348 + EpochOriginContract.ONE_ROW_CHECK 349 + " = 1) " 350 + ")"; 351 352 /** 353 * Table to store classified topic to apps mapping. In an epoch, an app is a contributor to a 354 * topic if the app has called Topics API in this epoch and is classified to the topic. 355 */ 356 public interface TopicContributorsContract { 357 String TABLE = TOPICS_TABLE_PREFIX + "topic_contributors"; 358 String ID = "_id"; 359 String EPOCH_ID = "epoch_id"; 360 String TOPIC = "topic"; 361 String APP = "app"; 362 } 363 364 /** The SQLite query to create topic_contributors table if it doesn't exist */ 365 public static final String CREATE_TABLE_TOPIC_CONTRIBUTORS = 366 "CREATE TABLE IF NOT EXISTS " 367 + TopicContributorsContract.TABLE 368 + "(" 369 + TopicContributorsContract.ID 370 + " INTEGER PRIMARY KEY, " 371 + TopicContributorsContract.EPOCH_ID 372 + " INTEGER NOT NULL, " 373 + TopicContributorsContract.TOPIC 374 + " INTEGER NOT NULL, " 375 + AppUsageHistoryContract.APP 376 + " TEXT NOT NULL" 377 + ")"; 378 379 /** Consolidated list of create statements for all tables. */ 380 public static final List<String> CREATE_STATEMENTS = 381 Collections.unmodifiableList( 382 Arrays.asList( 383 CREATE_TABLE_TOPICS_TAXONOMY, 384 CREATE_TABLE_APP_CLASSIFICATION_TOPICS, 385 CREATE_TABLE_TOP_TOPICS, 386 CREATE_TABLE_RETURNED_TOPIC, 387 CREATE_TABLE_RETURNED_ENCRYPTED_TOPIC, 388 CREATE_TABLE_USAGE_HISTORY, 389 CREATE_TABLE_APP_USAGE_HISTORY, 390 CREATE_TABLE_CALLER_CAN_LEARN_TOPICS, 391 CREATE_TABLE_BLOCKED_TOPICS, 392 CREATE_TABLE_EPOCH_ORIGIN, 393 CREATE_TABLE_TOPIC_CONTRIBUTORS)); 394 // ******************************************************************************************* 395 // * NOTE: Please check below steps before adding a new table: 396 // * 1) TopicsDao -> ALL_TOPICS_TABLES: User Consent to clear all tables 397 // * 2) EpochManager -> TABLE_INFO_FOR_EPOCH_GARBAGE_COLLECTION: GC for dat of old epochs. 398 // * 3) AppUpdateManager -> TABLE_INFO_TO_ERASE_APP_DATA: clear app data for app uninstallation 399 // * 4) DbHelper -> onUpgrade: Handle any new schema change 400 // ******************************************************************************************* 401 402 // Private constructor to prevent instantiation. TopicsTables()403 private TopicsTables() {} 404 } 405