• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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