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