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;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.util.Slog;
22 
23 import com.android.server.healthconnect.storage.datatypehelpers.AccessLogsHelper;
24 import com.android.server.healthconnect.storage.datatypehelpers.ActivityDateHelper;
25 import com.android.server.healthconnect.storage.datatypehelpers.AppInfoHelper;
26 import com.android.server.healthconnect.storage.datatypehelpers.ChangeLogsHelper;
27 import com.android.server.healthconnect.storage.datatypehelpers.ChangeLogsRequestHelper;
28 import com.android.server.healthconnect.storage.datatypehelpers.HealthDataCategoryPriorityHelper;
29 import com.android.server.healthconnect.storage.datatypehelpers.PreferenceHelper;
30 import com.android.server.healthconnect.storage.request.DeleteTableRequest;
31 import com.android.server.healthconnect.storage.request.DeleteTransactionRequest;
32 import com.android.server.healthconnect.storage.utils.RecordHelperProvider;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 /**
38  * A service that is run periodically to handle deletion of stale entries in HC DB.
39  *
40  * @hide
41  */
42 public class AutoDeleteService {
43     private static final String AUTO_DELETE_DURATION_RECORDS_KEY =
44             "auto_delete_duration_records_key";
45     private static final String TAG = "HealthConnectAutoDelete";
46 
47     /** Gets auto delete period for automatically deleting record entries */
getRecordRetentionPeriodInDays()48     public static int getRecordRetentionPeriodInDays() {
49         String result =
50                 PreferenceHelper.getInstance().getPreference(AUTO_DELETE_DURATION_RECORDS_KEY);
51 
52         if (result == null) return 0;
53         return Integer.parseInt(result);
54     }
55 
56     /** Sets auto delete period for automatically deleting record entries */
setRecordRetentionPeriodInDays(int days)57     public static void setRecordRetentionPeriodInDays(int days) {
58         PreferenceHelper.getInstance()
59                 .insertOrReplacePreference(AUTO_DELETE_DURATION_RECORDS_KEY, String.valueOf(days));
60     }
61 
62     /** Starts the Auto Deletion process. */
startAutoDelete(@onNull Context context)63     public static void startAutoDelete(@NonNull Context context) {
64         try {
65             // Only do transactional operations here - as this job might get cancelled for several
66             // reasons, such as: User switch, low battery etc.
67             deleteStaleRecordEntries();
68             deleteStaleChangeLogEntries();
69             deleteStaleAccessLogEntries();
70             // Update the recordTypesUsed by packages if required after the deletion of records.
71             AppInfoHelper.getInstance().syncAppInfoRecordTypesUsed();
72             // Re-sync activity dates table
73             ActivityDateHelper.reSyncForAllRecords();
74             // Sync health data priority list table
75             HealthDataCategoryPriorityHelper.getInstance().reSyncHealthDataPriorityTable(context);
76         } catch (Exception e) {
77             Slog.e(TAG, "Auto delete run failed", e);
78             // Don't rethrow as that will crash system_server
79         }
80     }
81 
deleteStaleRecordEntries()82     private static void deleteStaleRecordEntries() {
83         String recordAutoDeletePeriodString =
84                 PreferenceHelper.getInstance().getPreference(AUTO_DELETE_DURATION_RECORDS_KEY);
85         int recordAutoDeletePeriod =
86                 recordAutoDeletePeriodString == null
87                         ? 0
88                         : Integer.parseInt(recordAutoDeletePeriodString);
89         if (recordAutoDeletePeriod != 0) {
90             // 0 represents that no period is set,to delete only if not 0 else don't do anything
91             List<DeleteTableRequest> deleteTableRequests = new ArrayList<>();
92             RecordHelperProvider.getRecordHelpers()
93                     .values()
94                     .forEach(
95                             (recordHelper) -> {
96                                 DeleteTableRequest request =
97                                         recordHelper.getDeleteRequestForAutoDelete(
98                                                 recordAutoDeletePeriod);
99                                 deleteTableRequests.add(request);
100                             });
101             try {
102                 TransactionManager.getInitialisedInstance()
103                         .deleteAll(new DeleteTransactionRequest(deleteTableRequests));
104             } catch (Exception exception) {
105                 Slog.e(TAG, "Auto delete for records failed", exception);
106                 // Don't rethrow as that will crash system_server
107             }
108         }
109     }
110 
deleteStaleChangeLogEntries()111     private static void deleteStaleChangeLogEntries() {
112         try {
113             TransactionManager.getInitialisedInstance()
114                     .deleteWithoutChangeLogs(
115                             List.of(
116                                     ChangeLogsHelper.getDeleteRequestForAutoDelete(),
117                                     ChangeLogsRequestHelper.getDeleteRequestForAutoDelete()));
118         } catch (Exception exception) {
119             Slog.e(TAG, "Auto delete for Change logs failed", exception);
120             // Don't rethrow as that will crash system_server
121         }
122     }
123 
deleteStaleAccessLogEntries()124     private static void deleteStaleAccessLogEntries() {
125         try {
126             TransactionManager.getInitialisedInstance()
127                     .deleteWithoutChangeLogs(
128                             List.of(AccessLogsHelper.getDeleteRequestForAutoDelete()));
129         } catch (Exception exception) {
130             Slog.e(TAG, "Auto delete for Access logs failed", exception);
131             // Don't rethrow as that will crash system_server
132         }
133     }
134 }
135