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