• 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 android.health.connect.Constants.DELETE;
20 
21 import android.annotation.NonNull;
22 import android.health.connect.Constants;
23 import android.health.connect.RecordIdFilter;
24 import android.health.connect.aidl.DeleteUsingFiltersRequestParcel;
25 import android.health.connect.datatypes.RecordTypeIdentifier;
26 import android.health.connect.internal.datatypes.utils.RecordMapper;
27 import android.util.ArrayMap;
28 import android.util.ArraySet;
29 import android.util.Slog;
30 
31 import com.android.server.healthconnect.storage.datatypehelpers.AppInfoHelper;
32 import com.android.server.healthconnect.storage.datatypehelpers.ChangeLogsHelper;
33 import com.android.server.healthconnect.storage.datatypehelpers.RecordHelper;
34 import com.android.server.healthconnect.storage.utils.RecordHelperProvider;
35 import com.android.server.healthconnect.storage.utils.StorageUtils;
36 
37 import java.time.Instant;
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Objects;
43 import java.util.Set;
44 import java.util.UUID;
45 
46 /** @hide */
47 public final class DeleteTransactionRequest {
48     private static final String TAG = "HealthConnectDelete";
49     private final List<DeleteTableRequest> mDeleteTableRequests;
50     private final long mRequestingPackageNameId;
51     private ChangeLogsHelper.ChangeLogs mChangeLogs;
52     private boolean mHasHealthDataManagementPermission;
53 
DeleteTransactionRequest(String packageName, DeleteUsingFiltersRequestParcel request)54     public DeleteTransactionRequest(String packageName, DeleteUsingFiltersRequestParcel request) {
55         Objects.requireNonNull(packageName);
56         mDeleteTableRequests = new ArrayList<>(request.getRecordTypeFilters().size());
57         mRequestingPackageNameId = AppInfoHelper.getInstance().getAppInfoId(packageName);
58         if (request.usesIdFilters()) {
59             // We don't keep change logs for bulk deletes
60             mChangeLogs =
61                     new ChangeLogsHelper.ChangeLogs(
62                             DELETE, packageName, Instant.now().toEpochMilli());
63             List<RecordIdFilter> recordIds =
64                     request.getRecordIdFiltersParcel().getRecordIdFilters();
65             Set<UUID> uuidSet = new ArraySet<>();
66             Map<RecordHelper<?>, List<UUID>> recordTypeToUuids = new ArrayMap<>();
67             for (RecordIdFilter recordId : recordIds) {
68                 RecordHelper<?> recordHelper =
69                         RecordHelperProvider.getInstance()
70                                 .getRecordHelper(
71                                         RecordMapper.getInstance()
72                                                 .getRecordType(recordId.getRecordType()));
73                 UUID uuid = StorageUtils.getUUIDFor(recordId, packageName);
74                 if (uuidSet.contains(uuid)) {
75                     // id has been already been processed;
76                     continue;
77                 }
78                 recordTypeToUuids.putIfAbsent(recordHelper, new ArrayList<>());
79                 Objects.requireNonNull(recordTypeToUuids.get(recordHelper)).add(uuid);
80                 uuidSet.add(uuid);
81             }
82 
83             recordTypeToUuids.forEach(
84                     (recordHelper, uuids) ->
85                             mDeleteTableRequests.add(recordHelper.getDeleteTableRequest(uuids)));
86 
87             // We currently only support either using filters or ids, so if we are deleting using
88             // ids no need to proceed further.
89             return;
90         }
91 
92         // No ids are present, so we are good to go to use filters to process our request
93         List<Integer> recordTypeFilters = request.getRecordTypeFilters();
94         if (recordTypeFilters == null || recordTypeFilters.isEmpty()) {
95             recordTypeFilters =
96                     new ArrayList<>(
97                             RecordMapper.getInstance()
98                                     .getRecordIdToExternalRecordClassMap()
99                                     .keySet());
100         }
101 
102         recordTypeFilters.forEach(
103                 (recordType) -> {
104                     RecordHelper<?> recordHelper =
105                             RecordHelperProvider.getInstance().getRecordHelper(recordType);
106                     Objects.requireNonNull(recordHelper);
107 
108                     mDeleteTableRequests.add(
109                             recordHelper.getDeleteTableRequest(
110                                     request.getPackageNameFilters(),
111                                     request.getStartTime(),
112                                     request.getEndTime(),
113                                     request.isLocalTimeFilter()));
114                 });
115     }
116 
getDeleteTableRequests()117     public List<DeleteTableRequest> getDeleteTableRequests() {
118         if (Constants.DEBUG) {
119             Slog.d(TAG, "num of delete requests: " + mDeleteTableRequests.size());
120         }
121         return mDeleteTableRequests;
122     }
123 
124     /**
125      * Function to add an uuid corresponding to given pair of @recordType and @appId to
126      * recordTypeAndAppIdToUUIDMap of changeLogs
127      */
onRecordFetched( @ecordTypeIdentifier.RecordType int recordType, long appId, UUID uuid)128     public void onRecordFetched(
129             @RecordTypeIdentifier.RecordType int recordType, long appId, UUID uuid) {
130         if (mChangeLogs == null) {
131             return;
132         }
133         mChangeLogs.addUUID(recordType, appId, uuid);
134     }
135 
136     @NonNull
getChangeLogUpsertRequests()137     public List<UpsertTableRequest> getChangeLogUpsertRequests() {
138         if (mChangeLogs == null) {
139             return Collections.emptyList();
140         }
141 
142         return mChangeLogs.getUpsertTableRequests();
143     }
144 
enforcePackageCheck(UUID uuid, long appInfoId)145     public void enforcePackageCheck(UUID uuid, long appInfoId) {
146         if (mHasHealthDataManagementPermission) {
147             // Skip this check if the caller has data management permission
148             return;
149         }
150 
151         if (mRequestingPackageNameId != appInfoId) {
152             throw new IllegalArgumentException(
153                     mRequestingPackageNameId + " is not the owner for " + uuid);
154         }
155     }
156 
setHasManageHealthDataPermission( boolean hasHealthDataManagementPermission)157     public DeleteTransactionRequest setHasManageHealthDataPermission(
158             boolean hasHealthDataManagementPermission) {
159         mHasHealthDataManagementPermission = hasHealthDataManagementPermission;
160 
161         return this;
162     }
163 }
164