1 /*
2  * Copyright (C) 2021 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 package android.app.time.cts.shell;
17 
18 import static android.app.time.cts.shell.DeviceConfigKeys.LocationTimeZoneManager.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
19 import static android.app.time.cts.shell.DeviceConfigKeys.LocationTimeZoneManager.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
20 import static android.app.time.cts.shell.DeviceConfigKeys.NAMESPACE_SYSTEM_TIME;
21 
22 import static org.junit.Assume.assumeTrue;
23 
24 import java.io.BufferedReader;
25 import java.io.StringReader;
26 import java.util.Objects;
27 
28 /**
29  * A class for interacting with the {@code location_time_zone_manager} service via the shell "cmd"
30  * command-line interface.
31  */
32 public class LocationTimeZoneManagerShellHelper {
33     /**
34      * The index of the primary location time zone provider, used for shell commands.
35      */
36     public static final int PRIMARY_PROVIDER_INDEX = 0;
37 
38     /**
39      * The index of the secondary location time zone provider, used for shell commands.
40      */
41     public static final int SECONDARY_PROVIDER_INDEX = 1;
42 
43     /**
44      * The "disabled" provider mode (equivalent to there being no provider configured).
45      */
46     public static final String PROVIDER_MODE_DISABLED =
47             DeviceConfigKeys.LocationTimeZoneManager.PROVIDER_MODE_DISABLED;
48 
49     /**
50      * The "simulated" provider mode.
51      */
52     public static final String PROVIDER_MODE_SIMULATED =
53             DeviceConfigKeys.LocationTimeZoneManager.PROVIDER_MODE_SIMULATED;
54 
55     /**
56      * Simulated provider test command that simulates the bind succeeding.
57      */
58     public static final String SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND = "on_bind";
59 
60     /**
61      * Simulated provider test command that simulates the provider reporting uncertainty.
62      */
63     public static final String SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN = "uncertain";
64 
65     /**
66      * Simulated provider test command that simulates a successful time zone detection.
67      */
68     public static final String SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS = "success";
69 
70     /**
71      * Argument for {@link #SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS} to specify TZDB time zone IDs.
72      */
73     public static final String SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ = "tz";
74 
75     /**
76      * The name of the service for shell commands.
77      */
78     private static final String SERVICE_NAME = "location_time_zone_manager";
79 
80     /**
81      * A shell command that starts the service (after stop).
82      */
83     private static final String SHELL_COMMAND_START = "start";
84 
85     /**
86      * A shell command that stops the service.
87      */
88     private static final String SHELL_COMMAND_STOP = "stop";
89 
90     /**
91      * A shell command that tells the service to record state information during tests. The next
92      * argument value is "true" or "false".
93      */
94     private static final String SHELL_COMMAND_RECORD_PROVIDER_STATES = "record_provider_states";
95 
96     /**
97      * A shell command that tells the service to dump its current state.
98      */
99     private static final String SHELL_COMMAND_DUMP_STATE = "dump_state";
100 
101     /**
102      * Option for {@link #SHELL_COMMAND_DUMP_STATE} that tells it to dump state as a binary proto.
103      */
104     private static final String DUMP_STATE_OPTION_PROTO = "proto";
105 
106     /**
107      * A shell command that sends test commands to a provider
108      */
109     private static final String SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND =
110             "send_provider_test_command";
111 
112     private static final String SHELL_CMD_PREFIX = "cmd " + SERVICE_NAME + " ";
113 
114     private final DeviceShellCommandExecutor mShellCommandExecutor;
115     private final DeviceConfigShellHelper mDeviceConfigShellHelper;
116 
LocationTimeZoneManagerShellHelper(DeviceShellCommandExecutor shellCommandExecutor)117     public LocationTimeZoneManagerShellHelper(DeviceShellCommandExecutor shellCommandExecutor) {
118         mShellCommandExecutor = Objects.requireNonNull(shellCommandExecutor);
119         mDeviceConfigShellHelper = new DeviceConfigShellHelper(shellCommandExecutor);
120     }
121 
122     /**
123      * Throws an {@link org.junit.AssumptionViolatedException} if the location_time_zone_manager
124      * service is not found. The service can be turned off in config, so this can be used to prevent
125      * CTS tests that need it from running.
126      */
assumeLocationTimeZoneManagerIsPresent()127     public void assumeLocationTimeZoneManagerIsPresent() throws Exception {
128         assumeTrue(isLocationTimeZoneManagerPresent());
129     }
130 
131     /**
132      * Returns {@code false} if the location_time_zone_manager service is not found.
133      */
isLocationTimeZoneManagerPresent()134     public boolean isLocationTimeZoneManagerPresent() throws Exception {
135         // Look for the service name in "cmd -l".
136         String serviceList = mShellCommandExecutor.executeToString("cmd -l");
137         try (BufferedReader reader = new BufferedReader(new StringReader(serviceList))) {
138             String serviceName;
139             while ((serviceName = reader.readLine()) != null) {
140                 serviceName = serviceName.trim();
141                 if (SERVICE_NAME.equals(serviceName)) {
142                     return true;
143                 }
144             }
145             return false;
146         }
147     }
148 
149     /** Executes "start". Starts the service. */
start()150     public void start() throws Exception {
151         mShellCommandExecutor.executeToTrimmedString(SHELL_CMD_PREFIX + SHELL_COMMAND_START);
152     }
153 
154     /** Executes "stop". Stops the service. */
stop()155     public void stop() throws Exception {
156         mShellCommandExecutor.executeToTrimmedString(SHELL_CMD_PREFIX + SHELL_COMMAND_STOP);
157     }
158 
159     /** Executes "record_provider_states". */
recordProviderStates(boolean enabled)160     public void recordProviderStates(boolean enabled) throws Exception {
161         String cmd = String.format("%s %s", SHELL_COMMAND_RECORD_PROVIDER_STATES, enabled);
162         mShellCommandExecutor.executeToTrimmedString(SHELL_CMD_PREFIX + cmd);
163     }
164 
165     /** Executes "dump_state". */
dumpState()166     public byte[] dumpState() throws Exception {
167         String cmd = String.format("%s --%s", SHELL_COMMAND_DUMP_STATE, DUMP_STATE_OPTION_PROTO);
168         return mShellCommandExecutor.executeToBytes(SHELL_CMD_PREFIX + cmd);
169     }
170 
171     /** Modifies a provider's mode using "device_config" commands. */
setProviderModeOverride(int providerIndex, String mode)172     public void setProviderModeOverride(int providerIndex, String mode) throws Exception {
173         String deviceConfigKey;
174         if (providerIndex == PRIMARY_PROVIDER_INDEX) {
175             deviceConfigKey = KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
176         } else {
177             deviceConfigKey = KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
178         }
179 
180         if (mode == null) {
181             mDeviceConfigShellHelper.delete(NAMESPACE_SYSTEM_TIME, deviceConfigKey);
182         } else {
183             mDeviceConfigShellHelper.put(NAMESPACE_SYSTEM_TIME, deviceConfigKey, mode);
184         }
185     }
186 
187     /**
188      * Simulates a provider successfully binding using the "send_provider_test_command" command.
189      */
simulateProviderBind(int providerIndex)190     public void simulateProviderBind(int providerIndex) throws Exception {
191         sendProviderTestCommand(providerIndex, SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND);
192     }
193 
194     /**
195      * Simulates a provider generating an uncertain report using the "send_provider_test_command"
196      * command.
197      */
simulateProviderUncertain(int providerIndex)198     public void simulateProviderUncertain(int providerIndex) throws Exception {
199         sendProviderTestCommand(providerIndex, SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN);
200     }
201 
202     /**
203      * Simulates a provider generating a suggestion using the "send_provider_test_command" command.
204      */
simulateProviderSuggestion(int providerIndex, String... zoneIds)205     public void simulateProviderSuggestion(int providerIndex, String... zoneIds)
206             throws Exception {
207         String timeZoneIds = String.join("&", zoneIds);
208         String testCommand = String.format("%s %s=string_array:%s",
209                 SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS,
210                 SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ,
211                 timeZoneIds);
212         sendProviderTestCommand(providerIndex, testCommand);
213     }
214 
215     /** Executes "send_provider_test_command". */
sendProviderTestCommand(int providerIndex, String testCommand)216     private void sendProviderTestCommand(int providerIndex, String testCommand) throws Exception {
217         String cmd = String.format("%s %s %s",
218                 SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND, providerIndex, testCommand);
219         mShellCommandExecutor.executeToTrimmedString(SHELL_CMD_PREFIX + cmd);
220     }
221 }
222