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 output.add(createStatsEvent(atomTag, key, value, userId, 111 flagsData.mDataType)); 112 } 113 } 114 return output; 115 } 116 117 @NonNull logSecureSettings(Context context, int atomTag, int userId)118 static List<StatsEvent> logSecureSettings(Context context, int atomTag, int userId) { 119 final List<StatsEvent> output = new ArrayList<>(); 120 final ContentResolver resolver = context.getContentResolver(); 121 122 for (FlagsData flagsData : SECURE_SETTINGS) { 123 StringListParamProto proto = getList(flagsData.mFlagName); 124 if (proto == null) { 125 continue; 126 } 127 for (String key : proto.element) { 128 final String value = Settings.Secure.getStringForUser(resolver, key, userId); 129 output.add(createStatsEvent(atomTag, key, value, userId, 130 flagsData.mDataType)); 131 } 132 } 133 return output; 134 } 135 136 @VisibleForTesting 137 @Nullable getList(String flag)138 static StringListParamProto getList(String flag) { 139 final String base64 = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SETTINGS_STATS, flag); 140 if (TextUtils.isEmpty(base64)) { 141 return null; 142 } 143 final byte[] decode = Base64.decode(base64, Base64.NO_PADDING | Base64.NO_WRAP); 144 StringListParamProto list = null; 145 try { 146 list = StringListParamProto.parseFrom(decode); 147 } catch (Exception e) { 148 Slog.e(TAG, "Error parsing string list proto", e); 149 } 150 return list; 151 } 152 153 /** 154 * Create {@link StatsEvent} for SETTING_SNAPSHOT atom 155 */ 156 @NonNull createStatsEvent(int atomTag, String key, String value, int userId, int type)157 private static StatsEvent createStatsEvent(int atomTag, String key, String value, int userId, 158 int type) { 159 final StatsEvent.Builder builder = StatsEvent.newBuilder() 160 .setAtomId(atomTag) 161 .writeString(key); 162 boolean booleanValue = false; 163 int intValue = 0; 164 float floatValue = 0; 165 String stringValue = ""; 166 if (TextUtils.isEmpty(value)) { 167 builder.writeInt(FrameworkStatsLog.SETTING_SNAPSHOT__TYPE__NOTASSIGNED) 168 .writeBoolean(booleanValue) 169 .writeInt(intValue) 170 .writeFloat(floatValue) 171 .writeString(stringValue) 172 .writeInt(userId); 173 } else { 174 switch (type) { 175 case SETTING_SNAPSHOT__TYPE__ASSIGNED_BOOL_TYPE: 176 booleanValue = "1".equals(value); 177 break; 178 case FrameworkStatsLog.SETTING_SNAPSHOT__TYPE__ASSIGNED_INT_TYPE: 179 try { 180 intValue = Integer.parseInt(value); 181 } catch (NumberFormatException e) { 182 Slog.w(TAG, "Can not parse value to float: " + value); 183 } 184 break; 185 case SETTING_SNAPSHOT__TYPE__ASSIGNED_FLOAT_TYPE: 186 try { 187 floatValue = Float.parseFloat(value); 188 } catch (NumberFormatException e) { 189 Slog.w(TAG, "Can not parse value to float: " + value); 190 } 191 break; 192 case FrameworkStatsLog.SETTING_SNAPSHOT__TYPE__ASSIGNED_STRING_TYPE: 193 stringValue = value; 194 break; 195 default: 196 Slog.w(TAG, "Unexpected value type " + type); 197 } 198 builder.writeInt(type) 199 .writeBoolean(booleanValue) 200 .writeInt(intValue) 201 .writeFloat(floatValue) 202 .writeString(stringValue) 203 .writeInt(userId); 204 } 205 return builder.build(); 206 } 207 208 /** Class for defining flag name and its data type. */ 209 static final class FlagsData { 210 /** {@link DeviceConfig} flag name, value of the flag is {@link StringListParamProto} */ 211 String mFlagName; 212 /** Data type of the value getting from {@link Settings} keys. */ 213 int mDataType; 214 FlagsData(String flagName, int dataType)215 FlagsData(String flagName, int dataType) { 216 mFlagName = flagName; 217 mDataType = dataType; 218 } 219 } 220 } 221