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 } 188 189 /** Create Statement for the returned Topics table */ 190 @VisibleForTesting 191 public static final String CREATE_TABLE_RETURNED_TOPIC = 192 "CREATE TABLE " 193 + ReturnedTopicContract.TABLE 194 + "(" 195 + ReturnedTopicContract.ID 196 + " INTEGER PRIMARY KEY, " 197 + ReturnedTopicContract.EPOCH_ID 198 + " INTEGER NOT NULL, " 199 + ReturnedTopicContract.APP 200 + " TEXT NOT NULL, " 201 + ReturnedTopicContract.SDK 202 + " TEXT NOT NULL, " 203 + ReturnedTopicContract.TAXONOMY_VERSION 204 + " INTEGER NOT NULL, " 205 + ReturnedTopicContract.MODEL_VERSION 206 + " INTEGER NOT NULL, " 207 + ReturnedTopicContract.TOPIC 208 + " INTEGER NOT NULL" 209 + ")"; 210 211 /** 212 * Table to store the app/sdk usage history. Whenever an app or sdk calls the Topics API, one 213 * entry will be generated with the timestamp. 214 */ 215 public interface UsageHistoryContract { 216 String TABLE = TOPICS_TABLE_PREFIX + "usage_history"; 217 String EPOCH_ID = "epoch_id"; 218 String APP = "app"; 219 String SDK = "sdk"; 220 } 221 222 /** Create Statement for the Usage History table */ 223 @VisibleForTesting 224 public static final String CREATE_TABLE_USAGE_HISTORY = 225 "CREATE TABLE " 226 + UsageHistoryContract.TABLE 227 + "(" 228 + UsageHistoryContract.EPOCH_ID 229 + " INTEGER NOT NULL, " 230 + UsageHistoryContract.APP 231 + " TEXT NOT NULL, " 232 + UsageHistoryContract.SDK 233 + " TEXT" 234 + ")"; 235 236 /** 237 * Table to store history for app only Whenever an app calls the Topics API, one entry will be 238 * generated. 239 */ 240 public interface AppUsageHistoryContract { 241 String TABLE = TOPICS_TABLE_PREFIX + "app_usage_history"; 242 String ID = "_id"; 243 String EPOCH_ID = "epoch_id"; 244 String APP = "app"; 245 } 246 247 /** Create Statement for the Usage History App Only table */ 248 @VisibleForTesting 249 public static final String CREATE_TABLE_APP_USAGE_HISTORY = 250 "CREATE TABLE " 251 + AppUsageHistoryContract.TABLE 252 + "(" 253 + AppUsageHistoryContract.ID 254 + " INTEGER PRIMARY KEY, " 255 + AppUsageHistoryContract.EPOCH_ID 256 + " INTEGER NOT NULL, " 257 + AppUsageHistoryContract.APP 258 + " TEXT NOT NULL" 259 + ")"; 260 261 /** Table to store all blocked {@link Topic}s. Blocked topics are controlled by user. */ 262 public interface BlockedTopicsContract { 263 String TABLE = TOPICS_TABLE_PREFIX + "blocked"; 264 String ID = "_id"; 265 String TAXONOMY_VERSION = "taxonomy_version"; 266 String MODEL_VERSION = "model_version"; 267 String TOPIC = "topic"; 268 } 269 270 /** Create Statement for the blocked topics table. */ 271 @VisibleForTesting 272 public static final String CREATE_TABLE_BLOCKED_TOPICS = 273 "CREATE TABLE " 274 + BlockedTopicsContract.TABLE 275 + "(" 276 + BlockedTopicsContract.ID 277 + " INTEGER PRIMARY KEY, " 278 + BlockedTopicsContract.TAXONOMY_VERSION 279 + " INTEGER NOT NULL, " 280 + BlockedTopicsContract.MODEL_VERSION 281 + " INTEGER NOT NULL, " 282 + BlockedTopicsContract.TOPIC 283 + " INTEGER NOT NULL" 284 + ")"; 285 286 /** 287 * Table to store the original timestamp when the user calls Topics API. This table should have 288 * only 1 row that stores the origin. 289 */ 290 public interface EpochOriginContract { 291 String TABLE = TOPICS_TABLE_PREFIX + "epoch_origin"; 292 String ONE_ROW_CHECK = "one_row_check"; // to constrain 1 origin 293 String ORIGIN = "origin"; 294 } 295 296 /** 297 * At the first time inserting a record, it won't persist one_row_check field so that this first 298 * entry will have one_row_check = 1. Therefore, further persisting is not allowed as primary 299 * key cannot be duplicated value and one_row_check is constrained to only equal to 1 to forbid 300 * any increment. 301 */ 302 @VisibleForTesting 303 public static final String CREATE_TABLE_EPOCH_ORIGIN = 304 "CREATE TABLE " 305 + EpochOriginContract.TABLE 306 + "(" 307 + EpochOriginContract.ONE_ROW_CHECK 308 + " INTEGER PRIMARY KEY DEFAULT 1, " 309 + EpochOriginContract.ORIGIN 310 + " INTEGER NOT NULL, " 311 + "CONSTRAINT one_row_constraint CHECK (" 312 + EpochOriginContract.ONE_ROW_CHECK 313 + " = 1) " 314 + ")"; 315 316 /** 317 * Table to store classified topic to apps mapping. In an epoch, an app is a contributor to a 318 * topic if the app has called Topics API in this epoch and is classified to the topic. 319 */ 320 public interface TopicContributorsContract { 321 String TABLE = TOPICS_TABLE_PREFIX + "topic_contributors"; 322 String ID = "_id"; 323 String EPOCH_ID = "epoch_id"; 324 String TOPIC = "topic"; 325 String APP = "app"; 326 } 327 328 /** The SQLite query to create topic_contributors table if it doesn't exist */ 329 public static final String CREATE_TABLE_TOPIC_CONTRIBUTORS = 330 "CREATE TABLE IF NOT EXISTS " 331 + TopicContributorsContract.TABLE 332 + "(" 333 + TopicContributorsContract.ID 334 + " INTEGER PRIMARY KEY, " 335 + TopicContributorsContract.EPOCH_ID 336 + " INTEGER NOT NULL, " 337 + TopicContributorsContract.TOPIC 338 + " INTEGER NOT NULL, " 339 + AppUsageHistoryContract.APP 340 + " TEXT NOT NULL" 341 + ")"; 342 343 /** Consolidated list of create statements for all tables. */ 344 public static final List<String> CREATE_STATEMENTS = 345 Collections.unmodifiableList( 346 Arrays.asList( 347 CREATE_TABLE_TOPICS_TAXONOMY, 348 CREATE_TABLE_APP_CLASSIFICATION_TOPICS, 349 CREATE_TABLE_TOP_TOPICS, 350 CREATE_TABLE_RETURNED_TOPIC, 351 CREATE_TABLE_USAGE_HISTORY, 352 CREATE_TABLE_APP_USAGE_HISTORY, 353 CREATE_TABLE_CALLER_CAN_LEARN_TOPICS, 354 CREATE_TABLE_BLOCKED_TOPICS, 355 CREATE_TABLE_EPOCH_ORIGIN, 356 CREATE_TABLE_TOPIC_CONTRIBUTORS)); 357 // TODO(b/227393493): Should support a test if new table is added. 358 // ******************************************************************************************* 359 // * NOTE: Please check below steps before adding a new table: 360 // * 1) TopicsDao -> ALL_TOPICS_TABLES: User Consent to clear all tables 361 // * 2) EpochManager -> TABLE_INFO_FOR_EPOCH_GARBAGE_COLLECTION: GC for dat of old epochs. 362 // * 3) AppUpdateManager -> TABLE_INFO_TO_ERASE_APP_DATA: clear app data for app uninstallation 363 // * 4) DbHelper -> onUpgrade: Handle any new schema change 364 // ******************************************************************************************* 365 366 // Private constructor to prevent instantiation. TopicsTables()367 private TopicsTables() {} 368 } 369