• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.healthconnect.storage.datatypehelpers;
18 
19 import static com.android.server.healthconnect.storage.HealthConnectDatabase.createTable;
20 import static com.android.server.healthconnect.storage.datatypehelpers.RecordHelper.PRIMARY_COLUMN_NAME;
21 import static com.android.server.healthconnect.storage.utils.StorageUtils.INTEGER;
22 import static com.android.server.healthconnect.storage.utils.StorageUtils.INTEGER_NOT_NULL;
23 import static com.android.server.healthconnect.storage.utils.StorageUtils.PRIMARY;
24 import static com.android.server.healthconnect.storage.utils.StorageUtils.TEXT_NULL;
25 import static com.android.server.healthconnect.storage.utils.StorageUtils.getCursorInt;
26 import static com.android.server.healthconnect.storage.utils.StorageUtils.getCursorLong;
27 import static com.android.server.healthconnect.storage.utils.StorageUtils.getCursorString;
28 import static com.android.server.healthconnect.storage.utils.WhereClauses.LogicalOperator.AND;
29 
30 import android.annotation.Nullable;
31 import android.content.ContentValues;
32 import android.database.Cursor;
33 import android.database.sqlite.SQLiteDatabase;
34 import android.health.connect.datatypes.RecordTypeIdentifier;
35 import android.util.Pair;
36 
37 import com.android.server.healthconnect.storage.TransactionManager;
38 import com.android.server.healthconnect.storage.request.CreateTableRequest;
39 import com.android.server.healthconnect.storage.request.ReadTableRequest;
40 import com.android.server.healthconnect.storage.request.UpsertTableRequest;
41 import com.android.server.healthconnect.storage.utils.WhereClauses;
42 
43 import java.util.ArrayList;
44 import java.util.List;
45 
46 /**
47  * A class to interact with the DB table that stores the information about the back up requests i.e.
48  * {@code TABLE_NAME}
49  *
50  * <p>This class returns the row_id of the backup_token_table as a token, that can later be used to
51  * recreate the backup token.
52  *
53  * @hide
54  */
55 public class BackupChangeTokenHelper {
56     private static final String TABLE_NAME = "backup_change_token_table";
57     private static final String RECORD_TYPE_COLUMN_NAME = "record_type";
58     private static final String DATA_TABLE_PAGE_TOKEN_COLUMN_NAME = "data_table_page_token";
59     private static final String CHANGE_LOGS_REQUEST_TOKEN_COLUMN_NAME = "change_logs_request_token";
60 
61     /**
62      * @return the row Id for the backup_change_token_table.
63      */
getBackupChangeTokenRowId( TransactionManager transactionManager, @RecordTypeIdentifier.RecordType int recordType, long dataTablePageToken, @Nullable String changeLogsRequestToken)64     public static String getBackupChangeTokenRowId(
65             TransactionManager transactionManager,
66             @RecordTypeIdentifier.RecordType int recordType,
67             long dataTablePageToken,
68             @Nullable String changeLogsRequestToken) {
69         ContentValues contentValues = new ContentValues();
70 
71         contentValues.put(RECORD_TYPE_COLUMN_NAME, recordType);
72         contentValues.put(DATA_TABLE_PAGE_TOKEN_COLUMN_NAME, dataTablePageToken);
73         contentValues.put(CHANGE_LOGS_REQUEST_TOKEN_COLUMN_NAME, changeLogsRequestToken);
74 
75         return String.valueOf(
76                 transactionManager.insert(new UpsertTableRequest(TABLE_NAME, contentValues)));
77     }
78 
79     /** Reads the database and get backup change token. */
getBackupChangeToken( TransactionManager transactionManager, String token)80     public static BackupChangeToken getBackupChangeToken(
81             TransactionManager transactionManager, String token) {
82         ReadTableRequest readTableRequest =
83                 new ReadTableRequest(TABLE_NAME)
84                         .setWhereClause(
85                                 new WhereClauses(AND)
86                                         .addWhereEqualsClause(PRIMARY_COLUMN_NAME, token));
87         try (Cursor cursor = transactionManager.read(readTableRequest)) {
88             if (!cursor.moveToFirst()) {
89                 throw new IllegalArgumentException("Invalid backup change token");
90             }
91 
92             return new BackupChangeToken(
93                     getCursorInt(cursor, RECORD_TYPE_COLUMN_NAME),
94                     getCursorLong(cursor, DATA_TABLE_PAGE_TOKEN_COLUMN_NAME),
95                     getCursorString(cursor, CHANGE_LOGS_REQUEST_TOKEN_COLUMN_NAME));
96         }
97     }
98 
99     /** A class to represent the request corresponding to a backup change token. */
100     public static class BackupChangeToken {
101         private final @RecordTypeIdentifier.RecordType int mRecordType;
102         private final long mDataTablePageToken;
103         private final @Nullable String mChangeLogsRequestToken;
104 
105         /**
106          * @param recordType record type to be backed up next
107          * @param dataTablePageToken page token for the data table to be backed up
108          * @param changeLogsRequestToken row id in change logs request table to get token for change
109          *     logs table
110          */
BackupChangeToken( @ecordTypeIdentifier.RecordType int recordType, long dataTablePageToken, @Nullable String changeLogsRequestToken)111         public BackupChangeToken(
112                 @RecordTypeIdentifier.RecordType int recordType,
113                 long dataTablePageToken,
114                 @Nullable String changeLogsRequestToken) {
115             mRecordType = recordType;
116             mDataTablePageToken = dataTablePageToken;
117             mChangeLogsRequestToken = changeLogsRequestToken;
118         }
119 
120         /**
121          * Returns the record type to be backed up next.
122          *
123          * <p>Set to 0 before a complete full backup or for an incremental backup.
124          */
getRecordType()125         public @RecordTypeIdentifier.RecordType int getRecordType() {
126             return mRecordType;
127         }
128 
129         /**
130          * Returns the page token for the data table to be backed up if the data type is not
131          * RECORD_TYPE_UNKNOWN.
132          *
133          * <p>If the data type name is 0, returns -1.
134          */
getDataTablePageToken()135         public long getDataTablePageToken() {
136             return mDataTablePageToken;
137         }
138 
139         /**
140          * Returns the row id in the change logs request table to for retrieving the token in the
141          * change log table.
142          */
getChangeLogsRequestToken()143         public @Nullable String getChangeLogsRequestToken() {
144             return mChangeLogsRequestToken;
145         }
146     }
147 
148     /** Creates the backup token table. */
applyBackupTokenUpgrade(SQLiteDatabase db)149     public static void applyBackupTokenUpgrade(SQLiteDatabase db) {
150         createTable(db, getCreateTableRequest());
151     }
152 
153     /**
154      * @return the table name.
155      */
getTableName()156     public static String getTableName() {
157         return TABLE_NAME;
158     }
159 
getMainTableName()160     protected String getMainTableName() {
161         return TABLE_NAME;
162     }
163 
getCreateTableRequest()164     private static CreateTableRequest getCreateTableRequest() {
165         return new CreateTableRequest(TABLE_NAME, getColumnInfo());
166     }
167 
getColumnInfo()168     private static List<Pair<String, String>> getColumnInfo() {
169         List<Pair<String, String>> columnInfo = new ArrayList<>();
170         columnInfo.add(new Pair<>(PRIMARY_COLUMN_NAME, PRIMARY));
171         columnInfo.add(new Pair<>(RECORD_TYPE_COLUMN_NAME, INTEGER_NOT_NULL));
172         columnInfo.add(new Pair<>(DATA_TABLE_PAGE_TOKEN_COLUMN_NAME, INTEGER));
173         columnInfo.add(new Pair<>(CHANGE_LOGS_REQUEST_TOKEN_COLUMN_NAME, TEXT_NULL));
174         return columnInfo;
175     }
176 }
177