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 android.health.connect.Constants.DEFAULT_LONG; 20 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_UNKNOWN; 21 22 import static com.android.server.healthconnect.storage.utils.WhereClauses.LogicalOperator.AND; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.health.connect.Constants; 27 import android.health.connect.datatypes.RecordTypeIdentifier; 28 import android.util.Slog; 29 30 import com.android.server.healthconnect.storage.utils.StorageUtils; 31 import com.android.server.healthconnect.storage.utils.WhereClauses; 32 33 import java.util.Collections; 34 import java.util.List; 35 import java.util.Objects; 36 37 /** 38 * No need to have delete-requests for child tables as ideally they should be following cascaded 39 * deletes. If not please rethink the table structure and if possible remove the parent-child 40 * relationship. 41 * 42 * @hide 43 */ 44 public class DeleteTableRequest { 45 46 private static final String TAG = "HealthConnectDelete"; 47 private final String mTableName; 48 @RecordTypeIdentifier.RecordType private final int mRecordType; 49 50 private String mIdColumnName; 51 private String mPackageColumnName; 52 private String mTimeColumnName; 53 private List<Long> mPackageFilters; 54 private long mStartTime = DEFAULT_LONG; 55 private long mEndTime = DEFAULT_LONG; 56 private boolean mRequiresUuId; 57 private List<String> mIds; 58 private boolean mEnforcePackageCheck; 59 private int mNumberOfUuidsToDelete; 60 private WhereClauses mCustomWhereClauses; 61 private long mLessThanOrEqualValue; 62 63 @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression DeleteTableRequest( @onNull String tableName, @RecordTypeIdentifier.RecordType int recordType)64 public DeleteTableRequest( 65 @NonNull String tableName, @RecordTypeIdentifier.RecordType int recordType) { 66 Objects.requireNonNull(tableName); 67 68 mTableName = tableName; 69 mRecordType = recordType; 70 } 71 72 @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression DeleteTableRequest(@onNull String tableName)73 public DeleteTableRequest(@NonNull String tableName) { 74 Objects.requireNonNull(tableName); 75 76 mTableName = tableName; 77 mRecordType = RECORD_TYPE_UNKNOWN; 78 } 79 getPackageColumnName()80 public String getPackageColumnName() { 81 return mPackageColumnName; 82 } 83 requiresPackageCheck()84 public boolean requiresPackageCheck() { 85 return mEnforcePackageCheck; 86 } 87 setEnforcePackageCheck( String packageColumnName, String uuidColumnName)88 public DeleteTableRequest setEnforcePackageCheck( 89 String packageColumnName, String uuidColumnName) { 90 mEnforcePackageCheck = true; 91 mPackageColumnName = packageColumnName; 92 mIdColumnName = uuidColumnName; 93 return this; 94 } 95 setIds(@onNull String idColumnName, @NonNull List<String> ids)96 public DeleteTableRequest setIds(@NonNull String idColumnName, @NonNull List<String> ids) { 97 Objects.requireNonNull(ids); 98 Objects.requireNonNull(idColumnName); 99 100 mIds = ids.stream().map(StorageUtils::getNormalisedString).toList(); 101 mIdColumnName = idColumnName; 102 return this; 103 } 104 setId(@onNull String idColumnName, @NonNull String id)105 public DeleteTableRequest setId(@NonNull String idColumnName, @NonNull String id) { 106 Objects.requireNonNull(id); 107 Objects.requireNonNull(idColumnName); 108 109 mIds = Collections.singletonList(StorageUtils.getNormalisedString(id)); 110 mIdColumnName = idColumnName; 111 return this; 112 } 113 requiresRead()114 public boolean requiresRead() { 115 return mRequiresUuId || mEnforcePackageCheck; 116 } 117 setRequiresUuId(@onNull String idColumnName)118 public DeleteTableRequest setRequiresUuId(@NonNull String idColumnName) { 119 Objects.requireNonNull(idColumnName); 120 121 mRequiresUuId = true; 122 mIdColumnName = idColumnName; 123 124 return this; 125 } 126 getRecordType()127 public int getRecordType() { 128 return mRecordType; 129 } 130 131 @Nullable getIdColumnName()132 public String getIdColumnName() { 133 return mIdColumnName; 134 } 135 136 @Nullable getIds()137 public List<String> getIds() { 138 return mIds; 139 } 140 141 @NonNull getTableName()142 public String getTableName() { 143 return mTableName; 144 } 145 146 @NonNull setPackageFilter( String packageColumnName, List<Long> packageFilters)147 public DeleteTableRequest setPackageFilter( 148 String packageColumnName, List<Long> packageFilters) { 149 mPackageFilters = packageFilters; 150 mPackageColumnName = packageColumnName; 151 152 return this; 153 } 154 155 @NonNull getDeleteCommand()156 public String getDeleteCommand() { 157 return "DELETE FROM " + mTableName + getWhereCommand(); 158 } 159 getReadCommand()160 public String getReadCommand() { 161 return "SELECT " 162 + mIdColumnName 163 + ", " 164 + mPackageColumnName 165 + " FROM " 166 + mTableName 167 + getWhereCommand(); 168 } 169 getWhereCommand()170 public String getWhereCommand() { 171 WhereClauses whereClauses = 172 Objects.isNull(mCustomWhereClauses) ? new WhereClauses(AND) : mCustomWhereClauses; 173 whereClauses.addWhereInLongsClause(mPackageColumnName, mPackageFilters); 174 whereClauses.addWhereBetweenTimeClause(mTimeColumnName, mStartTime, mEndTime); 175 whereClauses.addWhereInClauseWithoutQuotes(mIdColumnName, mIds); 176 177 if (Constants.DEBUG) { 178 Slog.d( 179 TAG, 180 "delete query: tableName: " 181 + mTableName 182 + " whereClause: " 183 + whereClauses.get(true)); 184 } 185 186 return whereClauses.get(true); 187 } 188 189 @NonNull setTimeFilter( @onNull String timeColumnName, long startTime, long endTime)190 public DeleteTableRequest setTimeFilter( 191 @NonNull String timeColumnName, long startTime, long endTime) { 192 Objects.requireNonNull(timeColumnName); 193 194 // Return if the params will result in no impact on the query 195 if (startTime < 0 || endTime < startTime) { 196 return this; 197 } 198 199 mStartTime = startTime; 200 mEndTime = endTime; 201 mTimeColumnName = timeColumnName; 202 203 return this; 204 } 205 206 /** 207 * Sets total number of UUIDs being deleted by this request. 208 * 209 * @param numberOfUuidsToDelete Number of UUIDs being deleted 210 */ setNumberOfUuidsToDelete(int numberOfUuidsToDelete)211 public void setNumberOfUuidsToDelete(int numberOfUuidsToDelete) { 212 this.mNumberOfUuidsToDelete = numberOfUuidsToDelete; 213 } 214 215 /** 216 * Total number of records deleted. 217 * 218 * @return Number of records deleted by this request 219 */ getTotalNumberOfRecordsDeleted()220 public int getTotalNumberOfRecordsDeleted() { 221 if (requiresRead()) { 222 return mNumberOfUuidsToDelete; 223 } 224 return mIds.size(); 225 } 226 } 227