blob: 276938b6221ea45d1159f11eb809db1638c9494c [file] [log] [blame]
Nils Fürstea8263f42020-11-23 14:45:15 +01001# osmo_gsm_tester: specifics for running an AndroidUE modem
2#
3# Copyright (C) 2020 by Software Radio Systems Limited
4#
5# Author: Nils Fürste <nils.fuerste@softwareradiosystems.com>
6# Author: Bedran Karakoc <bedran.karakoc@softwareradiosystems.com>
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21import pprint
22
23from ..core import log, util, config, remote, schema, process
24from .run_node import RunNode
25from .ms import MS
26from .srslte_common import srslte_common
27from ..core.event_loop import MainLoop
28from .ms_srs import srsUEMetrics
29from .android_bitrate_monitor import BitRateMonitor
30from . import qc_diag
31from .android_apn import AndroidApn
32from .android_host import AndroidHost
33
34
35def on_register_schemas():
36 resource_schema = {
37 'additional_args[]': schema.STR,
38 'enable_pcap': schema.BOOL_STR,
39 }
40 for key, val in RunNode.schema().items():
41 resource_schema['run_node.%s' % key] = val
42 for key, val in AndroidApn.schema().items():
43 resource_schema['apn.%s' % key] = val
44 schema.register_resource_schema('modem', resource_schema)
45
46 config_schema = {
47 'enable_pcap': schema.BOOL_STR,
48 'log_all_level': schema.STR,
49 }
50 schema.register_config_schema('modem', config_schema)
51
52
53class AndroidUE(MS, AndroidHost, srslte_common):
54
55 REMOTEDIR = '/osmo-gsm-tester-androidue'
56 METRICSFILE = 'android_ue_metrics.csv'
57 PCAPFILE = 'android_ue.pcap'
58
59##############
60# PROTECTED
61##############
62 def __init__(self, testenv, conf):
63 self._run_node = RunNode.from_conf(conf.get('run_node', {}))
64 self.apn_worker = AndroidApn.from_conf(conf.get('apn', {})) if conf.get('apn', {}) != {} else None
65 self.qc_diag_mon = qc_diag.QcDiag(testenv, conf)
66 super().__init__('androidue_%s' % self.addr(), testenv, conf)
67 srslte_common.__init__(self)
68 self.rem_host = None
69 self.run_dir = None
70 self.remote_run_dir = None
71 self.emm_connected = False
72 self.rrc_connected = False
73 self.conn_reset_intvl = 20 # sec
74 self.connect_timeout = 300 # sec
75 self.enable_pcap = None
76 self.remote_pcap_file = None
77 self.pcap_file = None
78 self.data_interface = None
79 self.remote_metrics_file = None
80 self.metrics_file = None
81 self.brate_mon = None
Andre Puschmannb190b4c2021-03-08 18:15:27 +010082 self.num_carriers = 1
Nils Fürstea8263f42020-11-23 14:45:15 +010083
84 def configure(self):
85 values = dict(ue=config.get_defaults('androidue'))
86 config.overlay(values, dict(ue=self.testenv.suite().config().get('modem', {})))
87 config.overlay(values, dict(ue=self._conf))
88 self.dbg('AndroidUE CONFIG:\n' + pprint.pformat(values))
89
90 if 'qc_diag' in self.features():
91 self.enable_pcap = util.str2bool(values['ue'].get('enable_pcap', 'false'))
92
93 self.metrics_file = self.run_dir.child(AndroidUE.METRICSFILE)
94 self.pcap_file = self.run_dir.child(AndroidUE.PCAPFILE)
95 if not self._run_node.is_local():
96 self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr(), None,
97 self._run_node.ssh_port())
98 self.remote_run_dir = util.Dir(AndroidUE.REMOTEDIR)
99 self.remote_metrics_file = self.remote_run_dir.child(AndroidUE.METRICSFILE)
100 self.remote_pcap_file = self.remote_run_dir.child(AndroidUE.PCAPFILE)
101
102 if self.apn_worker:
103 self.apn_worker.configure(self.testenv, self.run_dir, self._run_node, self.rem_host)
104 # some Android UEs only accept new APNs when airplane mode is turned off
105 self.set_airplane_mode(False)
106 self.apn_worker.set_apn()
107 MainLoop.sleep(1)
108 self.set_airplane_mode(True)
109
110 # clear old diag files
111 self._clear_diag_logs()
112
113 def _clear_diag_logs(self):
114 popen_args_clear_diag_logs = \
115 ['su', '-c', '\"rm -r /data/local/tmp/diag_logs/ || true\"']
116 clear_diag_logs_proc = self.run_androidue_cmd('clear-diag-logs', popen_args_clear_diag_logs)
117 clear_diag_logs_proc.launch_sync()
118
119 def verify_metric(self, value, operation='avg', metric='dl_brate', criterion='gt', window=1):
120 self.brate_mon.save_metrics(self.metrics_file)
121 metrics = srsUEMetrics(self.metrics_file)
122 return metrics.verify(value, operation, metric, criterion, window)
123
124 def set_airplane_mode(self, apm_state):
125 self.log("Setting airplane mode: " + str(apm_state))
126 popen_args = ['settings', 'put', 'global', 'airplane_mode_on', str(int(apm_state)), ';',
127 'wait $!;',
128 'su', '-c', '\"am broadcast -a android.intent.action.AIRPLANE_MODE\";']
129 proc = self.run_androidue_cmd('set-airplane-mode', popen_args)
130 proc.launch_sync()
131
132 def get_assigned_addr(self, ipv6=False):
133 ip_prefix = '172.16.0'
134 proc = self.run_androidue_cmd('get-assigned-addr', ['ip', 'addr', 'show'])
135 proc.launch_sync()
136 out_l = proc.get_stdout().split('\n')
137 ip = ''
138 for line in out_l:
139 if ip_prefix in line:
140 ip = line.split(' ')[5][:-3]
141 self.data_interface = line.split(' ')[-1]
142 return ip
143
144########################
145# PUBLIC - INTERNAL API
146########################
147 def cleanup(self):
148 self.set_airplane_mode(True)
149
150 def addr(self):
151 return self._run_node.run_addr()
152
153 def run_node(self):
154 return self._run_node
155
156 def features(self):
157 return self._conf.get('features', [])
158
159###################
160# PUBLIC (test API included)
161###################
162 def run_netns_wait(self, name, popen_args):
163 # This function guarantees the compatibility with the current ping test. Please
164 # note that this function cannot execute commands on the machine the Android UE
165 # is attached to.
166 proc = self.run_androidue_cmd(name, popen_args)
167 proc.launch_sync()
168 return proc
169
170 def connect(self, enb):
171 self.log('Starting AndroidUE')
172 self.run_dir = util.Dir(self.testenv.test().get_run_dir().new_dir(self.name()))
173 self.configure()
174 CONN_CHK = 'osmo-gsm-tester_androidue_conn_chk.sh'
175
176 if 'qc_diag' in self.features():
177 self.qc_diag_mon.start()
178
179 if self._run_node.is_local():
180 popen_args_emm_conn_chk = [CONN_CHK, self._run_node.adb_serial_id(), '0', '0']
181 else:
182 popen_args_emm_conn_chk = [CONN_CHK, '0', self.rem_host.host(), self.rem_host.get_remote_port()]
183
184 # make sure osmo-gsm-tester_androidue_conn_chk.sh is available on the OGT master unit
185 name = 'emm-conn-chk'
186 run_dir = self.run_dir.new_dir(name)
187 emm_conn_chk_proc = process.Process(name, run_dir, popen_args_emm_conn_chk)
188 self.testenv.remember_to_stop(emm_conn_chk_proc)
189 emm_conn_chk_proc.launch()
190
191 # check connection status
192 timer = self.connect_timeout
193 while timer > 0:
194 if timer % self.conn_reset_intvl == 0:
195 self.set_airplane_mode(True)
196 MainLoop.sleep(1)
197 timer -= 1
198 self.set_airplane_mode(False)
199
200 if 'LTE' in emm_conn_chk_proc.get_stdout():
201 if not(self.get_assigned_addr() is ''):
202 self.emm_connected = True
203 self.rrc_connected = True
204 self.testenv.stop_process(emm_conn_chk_proc)
205 break
206
207 MainLoop.sleep(2)
208 timer -= 2
209
Nils Fürste7b4e1f62021-03-12 13:20:09 +0100210 if timer <= 0:
Nils Fürstea8263f42020-11-23 14:45:15 +0100211 raise log.Error('Connection timer of Android UE %s expired' % self._run_node.adb_serial_id())
212
213 self.brate_mon = BitRateMonitor(self.testenv, self.run_dir, self._run_node, self.rem_host, self.data_interface)
214 self.brate_mon.start()
215
216 def is_rrc_connected(self):
217 if not ('qc_diag' in self.features()):
218 raise log.Error('Monitoring RRC states not supported (missing qc_diag feature?)')
219
220 # if not self.qc_diag_mon.running():
221 # raise log.Error('Diag monitoring crashed or was not started')
222
223 rrc_state = self.qc_diag_mon.get_rrc_state()
224 if 'RRC_IDLE_CAMPED' in rrc_state:
225 self.rrc_connected = False
226 elif 'RRC_CONNECTED' in rrc_state:
227 self.rrc_connected = True
228 return self.rrc_connected
229
230 def is_registered(self, mcc_mnc=None):
231 if mcc_mnc:
232 raise log.Error('An AndroidUE cannot register to any predefined MCC/MNC')
233 return self.emm_connected
234
235 def get_counter(self, counter_name):
236 if counter_name == 'prach_sent':
237 # not implemented so far, return 2 to pass tests
238 return 2
239 elif counter_name == 'paging_received':
240 return self.qc_diag_mon.get_paging_counter()
241 else:
242 raise log.Error('Counter %s not implemented' % counter_name)
243
244 def netns(self):
245 return None