• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.backuprestore;
18 
19 import android.util.Slog;
20 
21 import com.android.internal.annotations.VisibleForTesting;
22 import com.android.server.healthconnect.proto.backuprestore.Settings;
23 import com.android.server.healthconnect.proto.backuprestore.Settings.AppInfo;
24 import com.android.server.healthconnect.proto.backuprestore.Settings.AutoDeleteFrequencyProto;
25 import com.android.server.healthconnect.proto.backuprestore.Settings.DistanceUnitProto;
26 import com.android.server.healthconnect.proto.backuprestore.Settings.EnergyUnitProto;
27 import com.android.server.healthconnect.proto.backuprestore.Settings.HeightUnitProto;
28 import com.android.server.healthconnect.proto.backuprestore.Settings.PriorityList;
29 import com.android.server.healthconnect.proto.backuprestore.Settings.TemperatureUnitProto;
30 import com.android.server.healthconnect.proto.backuprestore.Settings.WeightUnitProto;
31 import com.android.server.healthconnect.storage.datatypehelpers.AppInfoHelper;
32 import com.android.server.healthconnect.storage.datatypehelpers.HealthDataCategoryPriorityHelper;
33 import com.android.server.healthconnect.storage.datatypehelpers.PreferenceHelper;
34 
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.stream.Stream;
39 
40 /**
41  * Class that manages compiling the user settings into a proto.
42  *
43  * @hide
44  */
45 public final class CloudBackupSettingsHelper {
46 
47     private final HealthDataCategoryPriorityHelper mPriorityHelper;
48     private final PreferenceHelper mPreferenceHelper;
49     private final AppInfoHelper mAppInfoHelper;
50 
51     public static final String TAG = "CloudBackupSettingsHelper";
52 
53     public static final String ENERGY_UNIT_PREF_KEY = "ENERGY_UNIT_KEY";
54     public static final String TEMPERATURE_UNIT_PREF_KEY = "TEMPERATURE_UNIT_KEY";
55     public static final String HEIGHT_UNIT_PREF_KEY = "HEIGHT_UNIT_KEY";
56     public static final String WEIGHT_UNIT_PREF_KEY = "WEIGHT_UNIT_KEY";
57     public static final String DISTANCE_UNIT_PREF_KEY = "DISTANCE_UNIT_KEY";
58     public static final String AUTO_DELETE_PREF_KEY = "auto_delete_range_picker";
59 
CloudBackupSettingsHelper( HealthDataCategoryPriorityHelper priorityHelper, PreferenceHelper preferenceHelper, AppInfoHelper appInfoHelper)60     public CloudBackupSettingsHelper(
61             HealthDataCategoryPriorityHelper priorityHelper,
62             PreferenceHelper preferenceHelper,
63             AppInfoHelper appInfoHelper) {
64         mPriorityHelper = priorityHelper;
65         mPreferenceHelper = preferenceHelper;
66         mAppInfoHelper = appInfoHelper;
67     }
68 
69     /**
70      * Collate the user's priority list and unit preferences into a single object.
71      *
72      * @return the user's settings as a {@code Settings} object
73      */
collectUserSettings()74     public Settings collectUserSettings() {
75         Settings.Builder builder =
76                 Settings.newBuilder()
77                         .putAllAppInfo(getAppInfo())
78                         .putAllPriorityList(getPriorityList())
79                         .setAutoDeleteFrequency(getAutoDeleteSetting())
80                         .setEnergyUnitSetting(getEnergyPreference())
81                         .setTemperatureUnitSetting(getTemperaturePreference())
82                         .setHeightUnitSetting(getHeightPreference())
83                         .setWeightUnitSetting(getWeightPreference())
84                         .setDistanceUnitSetting(getDistancePreference());
85         return builder.build();
86     }
87 
88     /**
89      * Override the current settings with the provided new user settings, with the exception of the
90      * priority list which should be a merged version of the old and new priority list.
91      */
restoreUserSettings(Settings newUserSettings)92     public void restoreUserSettings(Settings newUserSettings) {
93         restoreAppInfo(newUserSettings.getAppInfoMap());
94         mergePriorityLists(newUserSettings.getPriorityListMap());
95         AutoDeleteFrequencyProto newAutoDeleteFrequency = newUserSettings.getAutoDeleteFrequency();
96         if (newAutoDeleteFrequency != AutoDeleteFrequencyProto.AUTO_DELETE_RANGE_UNSPECIFIED
97                 && newAutoDeleteFrequency != AutoDeleteFrequencyProto.UNRECOGNIZED) {
98             mPreferenceHelper.insertOrReplacePreference(
99                     AUTO_DELETE_PREF_KEY, newAutoDeleteFrequency.name());
100         }
101         EnergyUnitProto newEnergyUnit = newUserSettings.getEnergyUnitSetting();
102         if (newEnergyUnit != EnergyUnitProto.ENERGY_UNIT_UNSPECIFIED
103                 && newEnergyUnit != EnergyUnitProto.UNRECOGNIZED) {
104             mPreferenceHelper.insertOrReplacePreference(ENERGY_UNIT_PREF_KEY, newEnergyUnit.name());
105         }
106         TemperatureUnitProto newTemperatureUnit = newUserSettings.getTemperatureUnitSetting();
107         if (newTemperatureUnit != TemperatureUnitProto.TEMPERATURE_UNIT_UNSPECIFIED
108                 && newTemperatureUnit != TemperatureUnitProto.UNRECOGNIZED) {
109             mPreferenceHelper.insertOrReplacePreference(
110                     TEMPERATURE_UNIT_PREF_KEY, newTemperatureUnit.name());
111         }
112         HeightUnitProto newHeightUnit = newUserSettings.getHeightUnitSetting();
113         if (newHeightUnit != HeightUnitProto.HEIGHT_UNIT_UNSPECIFIED
114                 && newHeightUnit != HeightUnitProto.UNRECOGNIZED) {
115             mPreferenceHelper.insertOrReplacePreference(HEIGHT_UNIT_PREF_KEY, newHeightUnit.name());
116         }
117         WeightUnitProto newWeightUnit = newUserSettings.getWeightUnitSetting();
118         if (newWeightUnit != WeightUnitProto.WEIGHT_UNIT_UNSPECIFIED
119                 && newWeightUnit != WeightUnitProto.UNRECOGNIZED) {
120             mPreferenceHelper.insertOrReplacePreference(WEIGHT_UNIT_PREF_KEY, newWeightUnit.name());
121         }
122         DistanceUnitProto newDistanceUnit = newUserSettings.getDistanceUnitSetting();
123         if (newDistanceUnit != DistanceUnitProto.DISTANCE_UNIT_UNSPECIFIED
124                 && newDistanceUnit != DistanceUnitProto.UNRECOGNIZED) {
125             mPreferenceHelper.insertOrReplacePreference(
126                     DISTANCE_UNIT_PREF_KEY, newDistanceUnit.name());
127         }
128     }
129 
130     /**
131      * Restores a user's AppInfo settings from the passed in {@code Map<String, AppInfo>} object.
132      */
restoreAppInfo(Map<String, AppInfo> appInfoMap)133     void restoreAppInfo(Map<String, AppInfo> appInfoMap) {
134         for (var appInfoEntry : appInfoMap.entrySet()) {
135             String packageName = appInfoEntry.getKey();
136             AppInfo appInfo = appInfoEntry.getValue();
137             String appName = appInfo.hasAppName() ? appInfo.getAppName() : null;
138             mAppInfoHelper.restoreAppInfo(packageName, appName);
139         }
140     }
141 
142     /**
143      * Take two priority lists and merge them, removing any duplicate entries, and replace the
144      * existing priority list settings with this newly merged version.
145      *
146      * @param imported the new priority list being restored
147      */
148     @VisibleForTesting
mergePriorityLists(Map<Integer, PriorityList> imported)149     void mergePriorityLists(Map<Integer, PriorityList> imported) {
150         imported.forEach(
151                 (category, priorityListProto) -> {
152                     var packageNameList = priorityListProto.getPackageNameList();
153                     if (packageNameList.isEmpty()) {
154                         return;
155                     }
156 
157                     List<String> currentPriorityList =
158                             mAppInfoHelper.getPackageNames(
159                                     mPriorityHelper.getAppIdPriorityOrder(category));
160                     List<String> newPriorityList =
161                             Stream.concat(currentPriorityList.stream(), packageNameList.stream())
162                                     .distinct()
163                                     .toList();
164                     mPriorityHelper.setPriorityOrder(category, newPriorityList);
165                     Slog.d(
166                             TAG,
167                             "Added "
168                                     + packageNameList.size()
169                                     + " apps to priority list of category "
170                                     + category);
171                 });
172     }
173 
getPriorityList()174     private Map<Integer, PriorityList> getPriorityList() {
175         Map<Integer, List<Long>> priorityListMap =
176                 mPriorityHelper.getHealthDataCategoryToAppIdPriorityMapImmutable();
177         if (priorityListMap.isEmpty()) {
178             Slog.d(TAG, "Priority list is empty.");
179             return Map.of();
180         }
181         Map<Integer, PriorityList> protoFormattedPriorityList = new HashMap<>();
182         priorityListMap.forEach(
183                 (category, priorityList) -> {
184                     protoFormattedPriorityList.put(
185                             category,
186                             PriorityList.newBuilder()
187                                     .addAllPackageName(mAppInfoHelper.getPackageNames(priorityList))
188                                     .build());
189                 });
190         return protoFormattedPriorityList;
191     }
192 
getAppInfo()193     Map<String, AppInfo> getAppInfo() {
194         Map<String, AppInfo> appInfoMap = new HashMap<>();
195         for (var appInfoEntry : mAppInfoHelper.getAppInfoMap().entrySet()) {
196             String appName = appInfoEntry.getValue().getName();
197             AppInfo.Builder appInfoBuilder = AppInfo.newBuilder();
198             if (appName != null) {
199                 appInfoBuilder.setAppName(appName);
200             }
201             appInfoMap.putIfAbsent(appInfoEntry.getKey(), appInfoBuilder.build());
202         }
203         return appInfoMap;
204     }
205 
getAutoDeleteSetting()206     private AutoDeleteFrequencyProto getAutoDeleteSetting() {
207         String preference = mPreferenceHelper.getPreference(AUTO_DELETE_PREF_KEY);
208         return preference == null
209                 ? AutoDeleteFrequencyProto.AUTO_DELETE_RANGE_UNSPECIFIED
210                 : AutoDeleteFrequencyProto.valueOf(preference);
211     }
212 
getTemperaturePreference()213     private TemperatureUnitProto getTemperaturePreference() {
214         String preference = mPreferenceHelper.getPreference(TEMPERATURE_UNIT_PREF_KEY);
215         return preference == null
216                 ? TemperatureUnitProto.TEMPERATURE_UNIT_UNSPECIFIED
217                 : TemperatureUnitProto.valueOf(preference);
218     }
219 
getEnergyPreference()220     private EnergyUnitProto getEnergyPreference() {
221         String preference = mPreferenceHelper.getPreference(ENERGY_UNIT_PREF_KEY);
222         return preference == null
223                 ? EnergyUnitProto.ENERGY_UNIT_UNSPECIFIED
224                 : EnergyUnitProto.valueOf(preference);
225     }
226 
getHeightPreference()227     private HeightUnitProto getHeightPreference() {
228         String preference = mPreferenceHelper.getPreference(HEIGHT_UNIT_PREF_KEY);
229         return preference == null
230                 ? HeightUnitProto.HEIGHT_UNIT_UNSPECIFIED
231                 : HeightUnitProto.valueOf(preference);
232     }
233 
getWeightPreference()234     private WeightUnitProto getWeightPreference() {
235         String preference = mPreferenceHelper.getPreference(WEIGHT_UNIT_PREF_KEY);
236         return preference == null
237                 ? WeightUnitProto.WEIGHT_UNIT_UNSPECIFIED
238                 : WeightUnitProto.valueOf(preference);
239     }
240 
getDistancePreference()241     private DistanceUnitProto getDistancePreference() {
242         String preference = mPreferenceHelper.getPreference(DISTANCE_UNIT_PREF_KEY);
243         return preference == null
244                 ? DistanceUnitProto.DISTANCE_UNIT_UNSPECIFIED
245                 : DistanceUnitProto.valueOf(preference);
246     }
247 }
248