1 /* 2 * Copyright (C) 2020 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.stats.pull; 18 19 import static com.android.internal.util.FrameworkStatsLog.SETTING_SNAPSHOT__TYPE__ASSIGNED_BOOL_TYPE; 20 import static com.android.internal.util.FrameworkStatsLog.SETTING_SNAPSHOT__TYPE__ASSIGNED_FLOAT_TYPE; 21 import static com.android.internal.util.FrameworkStatsLog.SETTING_SNAPSHOT__TYPE__ASSIGNED_INT_TYPE; 22 import static com.android.internal.util.FrameworkStatsLog.SETTING_SNAPSHOT__TYPE__ASSIGNED_STRING_TYPE; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.content.ContentResolver; 27 import android.content.Context; 28 import android.provider.DeviceConfig; 29 import android.provider.Settings; 30 import android.text.TextUtils; 31 import android.util.Base64; 32 import android.util.Slog; 33 import android.util.StatsEvent; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 import com.android.internal.util.FrameworkStatsLog; 37 import com.android.service.nano.StringListParamProto; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 42 /** 43 * Utility methods for creating {@link StatsEvent} data. 44 */ 45 final class SettingsStatsUtil { 46 private static final String TAG = "SettingsStatsUtil"; 47 private static final FlagsData[] GLOBAL_SETTINGS = new FlagsData[]{ 48 new FlagsData("GlobalFeature__boolean_whitelist", 49 SETTING_SNAPSHOT__TYPE__ASSIGNED_BOOL_TYPE), 50 new FlagsData("GlobalFeature__integer_whitelist", 51 SETTING_SNAPSHOT__TYPE__ASSIGNED_INT_TYPE), 52 new FlagsData("GlobalFeature__float_whitelist", 53 SETTING_SNAPSHOT__TYPE__ASSIGNED_FLOAT_TYPE), 54 new FlagsData("GlobalFeature__string_whitelist", 55 SETTING_SNAPSHOT__TYPE__ASSIGNED_STRING_TYPE) 56 }; 57 private static final FlagsData[] SECURE_SETTINGS = new FlagsData[]{ 58 new FlagsData("SecureFeature__boolean_whitelist", 59 SETTING_SNAPSHOT__TYPE__ASSIGNED_BOOL_TYPE), 60 new FlagsData("SecureFeature__integer_whitelist", 61 SETTING_SNAPSHOT__TYPE__ASSIGNED_INT_TYPE), 62 new FlagsData("SecureFeature__float_whitelist", 63 SETTING_SNAPSHOT__TYPE__ASSIGNED_FLOAT_TYPE), 64 new FlagsData("SecureFeature__string_whitelist", 65 SETTING_SNAPSHOT__TYPE__ASSIGNED_STRING_TYPE) 66 }; 67 private static final FlagsData[] SYSTEM_SETTINGS = new FlagsData[]{ 68 new FlagsData("SystemFeature__boolean_whitelist", 69 SETTING_SNAPSHOT__TYPE__ASSIGNED_BOOL_TYPE), 70 new FlagsData("SystemFeature__integer_whitelist", 71 SETTING_SNAPSHOT__TYPE__ASSIGNED_INT_TYPE), 72 new FlagsData("SystemFeature__float_whitelist", 73 SETTING_SNAPSHOT__TYPE__ASSIGNED_FLOAT_TYPE), 74 new FlagsData("SystemFeature__string_whitelist", 75 SETTING_SNAPSHOT__TYPE__ASSIGNED_STRING_TYPE) 76 }; 77 78 @VisibleForTesting 79 @NonNull logGlobalSettings(Context context, int atomTag, int userId)80 static List<StatsEvent> logGlobalSettings(Context context, int atomTag, int userId) { 81 final List<StatsEvent> output = new ArrayList<>(); 82 final ContentResolver resolver = context.getContentResolver(); 83 84 for (FlagsData flagsData : GLOBAL_SETTINGS) { 85 StringListParamProto proto = getList(flagsData.mFlagName); 86 if (proto == null) { 87 continue; 88 } 89 for (String key : proto.element) { 90 final String value = Settings.Global.getStringForUser(resolver, key, userId); 91 output.add(createStatsEvent(atomTag, key, value, userId, 92 flagsData.mDataType)); 93 } 94 } 95 return output; 96 } 97 98 @NonNull logSystemSettings(Context context, int atomTag, int userId)99 static List<StatsEvent> logSystemSettings(Context context, int atomTag, int userId) { 100 final List<StatsEvent> output = new ArrayList<>(); 101 final ContentResolver resolver = context.getContentResolver(); 102 103 for (FlagsData flagsData : SYSTEM_SETTINGS) { 104 StringListParamProto proto = getList(flagsData.mFlagName); 105 if (proto == null) { 106 continue; 107 } 108 for (String key : proto.element) { 109 final String value = Settings.System.getStringForUser(resolver, key, userId); 110 final String telemetryValue = RawSettingsTelemetryUtils 111 .getTelemetrySettingFromRawVal(context, key, value); 112 output.add(createStatsEvent(atomTag, key, telemetryValue, userId, 113 flagsData.mDataType)); 114 } 115 } 116 return output; 117 } 118 119 @NonNull logSecureSettings(Context context, int atomTag, int userId)120 static List<StatsEvent> logSecureSettings(Context context, int atomTag, int userId) { 121 final List<StatsEvent> output = new ArrayList<>(); 122 final ContentResolver resolver = context.getContentResolver(); 123 124 for (FlagsData flagsData : SECURE_SETTINGS) { 125 StringListParamProto proto = getList(flagsData.mFlagName); 126 if (proto == null) { 127 continue; 128 } 129 for (String key : proto.element) { 130 final String value = Settings.Secure.getStringForUser(resolver, key, userId); 131 output.add(createStatsEvent(atomTag, key, value, userId, 132 flagsData.mDataType)); 133 } 134 } 135 return output; 136 } 137 138 @VisibleForTesting 139 @Nullable getList(String flag)140 static StringListParamProto getList(String flag) { 141 final String base64 = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SETTINGS_STATS, flag); 142 if (TextUtils.isEmpty(base64)) { 143 return null; 144 } 145 final byte[] decode = Base64.decode(base64, Base64.NO_PADDING | Base64.NO_WRAP); 146 StringListParamProto list = null; 147 try { 148 list = StringListParamProto.parseFrom(decode); 149 } catch (Exception e) { 150 Slog.e(TAG, "Error parsing string list proto", e); 151 } 152 return list; 153 } 154 155 /** 156 * Create {@link StatsEvent} for SETTING_SNAPSHOT atom 157 */ 158 @NonNull createStatsEvent(int atomTag, String key, String value, int userId, int type)159 private static StatsEvent createStatsEvent(int atomTag, String key, String value, int userId, 160 int type) { 161 final StatsEvent.Builder builder = StatsEvent.newBuilder() 162 .setAtomId(atomTag) 163 .writeString(key); 164 boolean booleanValue = false; 165 int intValue = 0; 166 float floatValue = 0; 167 String stringValue = ""; 168 if (TextUtils.isEmpty(value)) { 169 builder.writeInt(FrameworkStatsLog.SETTING_SNAPSHOT__TYPE__NOTASSIGNED) 170 .writeBoolean(booleanValue) 171 .writeInt(intValue) 172 .writeFloat(floatValue) 173 .writeString(stringValue) 174 .writeInt(userId); 175 } else { 176 switch (type) { 177 case SETTING_SNAPSHOT__TYPE__ASSIGNED_BOOL_TYPE: 178 booleanValue = "1".equals(value); 179 break; 180 case FrameworkStatsLog.SETTING_SNAPSHOT__TYPE__ASSIGNED_INT_TYPE: 181 try { 182 intValue = Integer.parseInt(value); 183 } catch (NumberFormatException e) { 184 Slog.w(TAG, "Can not parse value to float: " + value); 185 } 186 break; 187 case SETTING_SNAPSHOT__TYPE__ASSIGNED_FLOAT_TYPE: 188 try { 189 floatValue = Float.parseFloat(value); 190 } catch (NumberFormatException e) { 191 Slog.w(TAG, "Can not parse value to float: " + value); 192 } 193 break; 194 case FrameworkStatsLog.SETTING_SNAPSHOT__TYPE__ASSIGNED_STRING_TYPE: 195 stringValue = value; 196 break; 197 default: 198 Slog.w(TAG, "Unexpected value type " + type); 199 } 200 builder.writeInt(type) 201 .writeBoolean(booleanValue) 202 .writeInt(intValue) 203 .writeFloat(floatValue) 204 .writeString(stringValue) 205 .writeInt(userId); 206 } 207 return builder.build(); 208 } 209 210 /** Class for defining flag name and its data type. */ 211 static final class FlagsData { 212 /** {@link DeviceConfig} flag name, value of the flag is {@link StringListParamProto} */ 213 String mFlagName; 214 /** Data type of the value getting from {@link Settings} keys. */ 215 int mDataType; 216 FlagsData(String flagName, int dataType)217 FlagsData(String flagName, int dataType) { 218 mFlagName = flagName; 219 mDataType = dataType; 220 } 221 } 222 } 223