blob: 4cc8961d8424513a0e68e28421db917c0ef3c9fe [file] [log] [blame]
Yichun Li3e0ae062018-04-17 18:10:12 -07001/*
2 * Copyright (C) 2018 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 */
16package com.android.tradefed.suite.checker;
17
18import com.android.tradefed.config.Option;
19import com.android.tradefed.device.DeviceNotAvailableException;
20import com.android.tradefed.device.ITestDevice;
21import com.android.tradefed.log.LogUtil.CLog;
22import com.android.tradefed.suite.checker.StatusCheckerResult.CheckStatus;
23import com.android.tradefed.util.ArrayUtil;
24
25import com.google.common.collect.MapDifference;
26import com.google.common.collect.MapDifference.ValueDifference;
27import com.google.common.collect.Maps;
28
29import java.util.HashMap;
30import java.util.HashSet;
31import java.util.LinkedList;
32import java.util.List;
33import java.util.Map;
34import java.util.Set;
35
36/** Check if device settings have changed during a module run. */
37public class DeviceSettingChecker implements ISystemStatusChecker {
38
39 private static final String NAMESPACE_SYSTEM = "system";
40 private static final String NAMESPACE_GLOBAL = "global";
41 private static final String NAMESPACE_SECURE = "secure";
42 private static final Set<String> NAMESPACES = new HashSet<>();
43
44 static {
45 NAMESPACES.add(NAMESPACE_SYSTEM);
46 NAMESPACES.add(NAMESPACE_GLOBAL);
47 NAMESPACES.add(NAMESPACE_SECURE);
48 }
49
50 @Option(
51 name = "ignore-system-setting",
52 description = "Specify a system setting that's exempted from the checker; may be repeated"
53 )
54 private Set<String> mIgnoredSystemSettings = new HashSet<>();
55
56 @Option(
57 name = "ignore-secure-setting",
58 description = "Specify a secure setting that's exempted from the checker; may be repeated"
59 )
60 private Set<String> mIgnoredSecureSettings = new HashSet<>();
61
62 @Option(
63 name = "ignore-global-setting",
64 description = "Specify a global setting that's exempted from the checker; may be repeated"
65 )
66 private Set<String> mIgnoredGlobalSettings = new HashSet<>();
67
68 // The key of map is namespace, the value is settings' key-value map.
69 private Map<String, Map<String, String>> mSettingValPair = new HashMap<>();
70
71 /** {@inheritDoc} */
72 @Override
73 public StatusCheckerResult preExecutionCheck(ITestDevice device)
74 throws DeviceNotAvailableException {
75 mSettingValPair = new HashMap<>();
76 CLog.d("Begin preExecutionCheck for checking device setting");
77 Set<String> failedNamespaces = new HashSet<>();
78 for (String namespace : NAMESPACES) {
79 Map<String, String> newSettings = getSettingsHelper(device, namespace);
80 if (newSettings == null || newSettings.isEmpty()) {
81 failedNamespaces.add(namespace);
82 } else {
83 mSettingValPair.put(namespace, newSettings);
84 }
85 }
86 if (failedNamespaces.isEmpty()) {
87 return new StatusCheckerResult(CheckStatus.SUCCESS);
88 } else {
89 StatusCheckerResult result = new StatusCheckerResult(CheckStatus.FAILED);
90 String errorMessage =
91 String.format(
92 "Failed to get setting in pre-execution: %s\n",
93 String.join(",", failedNamespaces));
94 CLog.w(errorMessage);
95 result.setErrorMessage(errorMessage);
96 return result;
97 }
98 }
99
100 /** {@inheritDoc} */
101 @Override
102 public StatusCheckerResult postExecutionCheck(ITestDevice device)
103 throws DeviceNotAvailableException {
104 CLog.d("Begin postExecution for checking device setting");
105 Map<String, List<String>> failureMap = new HashMap<>();
106 for (String namespace : NAMESPACES) {
107 List<String> failureInfo = new LinkedList<>();
108 Map<String, String> newSettings = getSettingsHelper(device, namespace);
109 Map<String, String> oldSettings = mSettingValPair.get(namespace);
110 if (oldSettings == null) {
111 continue;
112 }
113 MapDifference<String, String> diff = Maps.difference(oldSettings, newSettings);
114 // Settings exist in both side but have different values.
115 Map<String, ValueDifference<String>> entriesDiff = diff.entriesDiffering();
116 for (Map.Entry<String, ValueDifference<String>> entry : entriesDiff.entrySet()) {
117 String failure =
118 String.format(
119 "%s changes from %s to %s",
120 entry.getKey(),
121 entry.getValue().leftValue(),
122 entry.getValue().rightValue());
123 failureInfo.add(failure);
124 }
125 // Settings only exist in the old one.
126 Set<String> settingsOnlyInOld = diff.entriesOnlyOnLeft().keySet();
127 if (!settingsOnlyInOld.isEmpty()) {
128 failureInfo.add(
129 String.format(
130 "%s is not found after module run",
131 String.join(",", settingsOnlyInOld)));
132 }
133 // Settings only exist in the new one.
134 Set<String> settingsOnlyInNew = diff.entriesOnlyOnRight().keySet();
135 if (!settingsOnlyInNew.isEmpty()) {
136 failureInfo.add(
137 String.format(
138 "%s is not found before module run",
139 String.join(",", settingsOnlyInNew)));
140 }
141 if (!failureInfo.isEmpty()) {
142 failureMap.put(namespace, failureInfo);
143 }
144 }
145
146 if (failureMap.isEmpty()) {
147 return new StatusCheckerResult(CheckStatus.SUCCESS);
148 } else {
149 StatusCheckerResult result = new StatusCheckerResult(CheckStatus.FAILED);
150 StringBuilder failureMessage = new StringBuilder();
151 for (String namespace : failureMap.keySet()) {
152 failureMessage.append(
153 String.format(
154 "Setting in namespace %s fails:\n %s\n",
155 namespace, ArrayUtil.join("\n", failureMap.get(namespace))));
156 }
157 CLog.w(failureMessage.toString());
158 result.setErrorMessage(failureMessage.toString());
159 return result;
160 }
161 }
162
163 /**
164 * Get all device settings given namespace, and remove ignored key from map.
165 *
166 * @param device The {@link ITestDevice}
167 * @param namespace namespace of the setting.
Julien Desprez82028932018-09-06 11:54:35 -0700168 * @return a Map of the settings for a namespace
Yichun Li3e0ae062018-04-17 18:10:12 -0700169 * @throws DeviceNotAvailableException
170 */
171 private Map<String, String> getSettingsHelper(ITestDevice device, String namespace)
172 throws DeviceNotAvailableException {
173 Map<String, String> settingsPair = device.getAllSettings(namespace);
174 Set<String> ignoredSettings = new HashSet<>();
175 switch (namespace) {
176 case NAMESPACE_GLOBAL:
177 ignoredSettings = mIgnoredGlobalSettings;
178 break;
179 case NAMESPACE_SECURE:
180 ignoredSettings = mIgnoredSecureSettings;
181 break;
182 case NAMESPACE_SYSTEM:
183 ignoredSettings = mIgnoredSystemSettings;
184 break;
185 }
186 for (String ignoredSetting : ignoredSettings) {
187 settingsPair.remove(ignoredSetting);
188 }
189 return settingsPair;
190 }
191}