• 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.server.healthconnect.storage.request;
18 
19 import static com.android.server.healthconnect.storage.utils.WhereClauses.LogicalOperator.OR;
20 
21 import android.annotation.IntDef;
22 import android.annotation.Nullable;
23 import android.content.ContentValues;
24 import android.database.Cursor;
25 import android.health.connect.datatypes.RecordTypeIdentifier;
26 import android.health.connect.internal.datatypes.RecordInternal;
27 import android.util.ArrayMap;
28 import android.util.Pair;
29 
30 import com.android.server.healthconnect.storage.datatypehelpers.RecordHelper;
31 import com.android.server.healthconnect.storage.utils.StorageUtils;
32 import com.android.server.healthconnect.storage.utils.TableColumnPair;
33 import com.android.server.healthconnect.storage.utils.WhereClauses;
34 
35 import java.lang.annotation.ElementType;
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.lang.annotation.Target;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.Objects;
42 
43 /** @hide */
44 public class UpsertTableRequest {
45     public static final int INVALID_ROW_ID = -1;
46 
47     public static final int TYPE_STRING = 0;
48     public static final int TYPE_BLOB = 1;
49     private final String mTable;
50     private ContentValues mContentValues;
51     private final List<Pair<String, Integer>> mUniqueColumns;
52     private List<UpsertTableRequest> mChildTableRequests = Collections.emptyList();
53     private String mParentCol;
54     private long mRowId = INVALID_ROW_ID;
55     private WhereClauses mWhereClausesForUpdate;
56     private IRequiresUpdate mRequiresUpdate = new IRequiresUpdate() {};
57     private Integer mRecordType;
58     private RecordInternal<?> mRecordInternal;
59     private RecordHelper<?> mRecordHelper;
60     private List<String> mPostUpsertCommands = Collections.emptyList();
61     private List<TableColumnPair> mChildTableAndColumnPairsToDelete = Collections.emptyList();
62 
63     @Nullable private ArrayMap<String, Boolean> mExtraWritePermissionsStateMapping;
64 
UpsertTableRequest(String table, ContentValues contentValues)65     public UpsertTableRequest(String table, ContentValues contentValues) {
66         this(table, contentValues, Collections.emptyList());
67     }
68 
69     @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression
UpsertTableRequest( String table, ContentValues contentValues, List<Pair<String, Integer>> uniqueColumns)70     public UpsertTableRequest(
71             String table, ContentValues contentValues, List<Pair<String, Integer>> uniqueColumns) {
72         Objects.requireNonNull(table);
73         Objects.requireNonNull(contentValues);
74         Objects.requireNonNull(uniqueColumns);
75 
76         mTable = table;
77         mContentValues = contentValues;
78         mUniqueColumns = uniqueColumns;
79     }
80 
getUniqueColumnsCount()81     public int getUniqueColumnsCount() {
82         return mUniqueColumns.size();
83     }
84 
withParentKey(long rowId)85     public UpsertTableRequest withParentKey(long rowId) {
86         mRowId = rowId;
87         return this;
88     }
89 
90     /**
91      * Use this if you want to add row_id of the parent table to all the child entries in {@code
92      * parentCol}
93      */
94     @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression
setParentColumnForChildTables(@ullable String parentCol)95     public UpsertTableRequest setParentColumnForChildTables(@Nullable String parentCol) {
96         mParentCol = parentCol;
97         return this;
98     }
99 
setRequiresUpdateClause(IRequiresUpdate requiresUpdate)100     public UpsertTableRequest setRequiresUpdateClause(IRequiresUpdate requiresUpdate) {
101         Objects.requireNonNull(requiresUpdate);
102 
103         mRequiresUpdate = requiresUpdate;
104         return this;
105     }
106 
getTable()107     public String getTable() {
108         return mTable;
109     }
110 
getContentValues()111     public ContentValues getContentValues() {
112         // Set the parent column of the creator of this requested to do that
113         if (!Objects.isNull(mParentCol) && mRowId != INVALID_ROW_ID) {
114             mContentValues.put(mParentCol, mRowId);
115         }
116 
117         return mContentValues;
118     }
119 
getChildTableRequests()120     public List<UpsertTableRequest> getChildTableRequests() {
121         return mChildTableRequests;
122     }
123 
setChildTableRequests(List<UpsertTableRequest> childTableRequests)124     public UpsertTableRequest setChildTableRequests(List<UpsertTableRequest> childTableRequests) {
125         Objects.requireNonNull(childTableRequests);
126 
127         mChildTableRequests = childTableRequests;
128         return this;
129     }
130 
getUpdateWhereClauses()131     public WhereClauses getUpdateWhereClauses() {
132         if (mWhereClausesForUpdate == null) {
133             return getReadWhereClauses();
134         }
135 
136         return mWhereClausesForUpdate;
137     }
138 
setUpdateWhereClauses(WhereClauses whereClauses)139     public UpsertTableRequest setUpdateWhereClauses(WhereClauses whereClauses) {
140         Objects.requireNonNull(whereClauses);
141 
142         mWhereClausesForUpdate = whereClauses;
143         return this;
144     }
145 
getReadRequest()146     public ReadTableRequest getReadRequest() {
147         return new ReadTableRequest(getTable()).setWhereClause(getReadWhereClauses());
148     }
149 
getReadRequestUsingUpdateClause()150     public ReadTableRequest getReadRequestUsingUpdateClause() {
151         return new ReadTableRequest(getTable()).setWhereClause(getUpdateWhereClauses());
152     }
153 
getReadWhereClauses()154     private WhereClauses getReadWhereClauses() {
155         WhereClauses readWhereClause = new WhereClauses(OR);
156 
157         for (Pair<String, Integer> uniqueColumn : mUniqueColumns) {
158             switch (uniqueColumn.second) {
159                 case TYPE_BLOB ->
160                         readWhereClause.addWhereEqualsClause(
161                                 uniqueColumn.first,
162                                 StorageUtils.getHexString(
163                                         mContentValues.getAsByteArray(uniqueColumn.first)));
164                 case TYPE_STRING ->
165                         readWhereClause.addWhereEqualsClause(
166                                 uniqueColumn.first, mContentValues.getAsString(uniqueColumn.first));
167                 default ->
168                         throw new UnsupportedOperationException(
169                                 "Unable to find type: " + uniqueColumn.second);
170             }
171         }
172 
173         return readWhereClause;
174     }
175 
requiresUpdate(Cursor cursor, UpsertTableRequest request)176     public boolean requiresUpdate(Cursor cursor, UpsertTableRequest request) {
177         return mRequiresUpdate.requiresUpdate(cursor, getContentValues(), request);
178     }
179 
getRowIdColName()180     public String getRowIdColName() {
181         return RecordHelper.PRIMARY_COLUMN_NAME;
182     }
183 
184     @RecordTypeIdentifier.RecordType
getRecordType()185     public int getRecordType() {
186         Objects.requireNonNull(mRecordType);
187         return mRecordType;
188     }
189 
setRecordType(@ecordTypeIdentifier.RecordType int recordIdentifier)190     public void setRecordType(@RecordTypeIdentifier.RecordType int recordIdentifier) {
191         mRecordType = recordIdentifier;
192     }
193 
setHelper( RecordHelper<?> recordHelper)194     public <T extends RecordInternal<?>> UpsertTableRequest setHelper(
195             RecordHelper<?> recordHelper) {
196         mRecordHelper = recordHelper;
197 
198         return this;
199     }
200 
201     /**
202      * Returns the {@link UpsertTableRequest} with the {@code childTableAndColumnPairsToDelete} set.
203      *
204      * @param childTableAndColumnPairsToDelete a list of {@link TableColumnPair}.
205      */
setChildTablesWithRowsToBeDeletedDuringUpdate( List<TableColumnPair> childTableAndColumnPairsToDelete)206     public UpsertTableRequest setChildTablesWithRowsToBeDeletedDuringUpdate(
207             List<TableColumnPair> childTableAndColumnPairsToDelete) {
208         mChildTableAndColumnPairsToDelete = childTableAndColumnPairsToDelete;
209         return this;
210     }
211 
212     /**
213      * Returns a list {@link TableColumnPair}s to be deleted when an update happens to the parent
214      * row.
215      */
getChildTablesWithRowsToBeDeletedDuringUpdate()216     public List<TableColumnPair> getChildTablesWithRowsToBeDeletedDuringUpdate() {
217         if (mRecordHelper != null) {
218             return mRecordHelper.getChildTablesWithRowsToBeDeletedDuringUpdate(
219                     mExtraWritePermissionsStateMapping);
220         }
221         return mChildTableAndColumnPairsToDelete;
222     }
223 
getAllChildTables()224     public List<String> getAllChildTables() {
225         return mRecordHelper == null ? Collections.emptyList() : mRecordHelper.getAllChildTables();
226     }
227 
getRecordInternal()228     public RecordInternal<?> getRecordInternal() {
229         return mRecordInternal;
230     }
231 
setRecordInternal(RecordInternal<?> recordInternal)232     public void setRecordInternal(RecordInternal<?> recordInternal) {
233         mRecordInternal = recordInternal;
234     }
235 
setExtraWritePermissionsStateMapping( @ullable ArrayMap<String, Boolean> extraWritePermissionsToState)236     public <T extends RecordInternal<?>> UpsertTableRequest setExtraWritePermissionsStateMapping(
237             @Nullable ArrayMap<String, Boolean> extraWritePermissionsToState) {
238         mExtraWritePermissionsStateMapping = extraWritePermissionsToState;
239         return this;
240     }
241 
242     /** Get SQL commands to be exected after this upsert has completed. */
getPostUpsertCommands()243     public List<String> getPostUpsertCommands() {
244         return mPostUpsertCommands;
245     }
246 
247     /** Set SQL commands to be exected after this upsert has completed. */
setPostUpsertCommands(List<String> commands)248     public UpsertTableRequest setPostUpsertCommands(List<String> commands) {
249         mPostUpsertCommands = commands;
250         return this;
251     }
252 
253     @Target(ElementType.TYPE_USE)
254     @Retention(RetentionPolicy.SOURCE)
255     @IntDef({TYPE_STRING, TYPE_BLOB})
256     public @interface ColumnType {}
257 
258     public interface IRequiresUpdate {
requiresUpdate( Cursor cursor, ContentValues contentValues, UpsertTableRequest request)259         default boolean requiresUpdate(
260                 Cursor cursor, ContentValues contentValues, UpsertTableRequest request) {
261             return true;
262         }
263     }
264 }
265