blob: 9fddff36e3ed3e3e515114bde473c439a6046c45 [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
82
83 def configure(self):
84 values = dict(ue=config.get_defaults('androidue'))
85 config.overlay(values, dict(ue=self.testenv.suite().config().get('modem', {})))
86 config.overlay(values, dict(ue=self._conf))
87 self.dbg('AndroidUE CONFIG:\n' + pprint.pformat(values))
88
89 if 'qc_diag' in self.features():
90 self.enable_pcap = util.str2bool(values['ue'].get('enable_pcap', 'false'))
91
92 self.metrics_file = self.run_dir.child(AndroidUE.METRICSFILE)
93 self.pcap_file = self.run_dir.child(AndroidUE.PCAPFILE)
94 if not self._run_node.is_local():
95 self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr(), None,
96 self._run_node.ssh_port())
97 self.remote_run_dir = util.Dir(AndroidUE.REMOTEDIR)
98 self.remote_metrics_file = self.remote_run_dir.child(AndroidUE.METRICSFILE)
99 self.remote_pcap_file = self.remote_run_dir.child(AndroidUE.PCAPFILE)
100
101 if self.apn_worker:
102 self.apn_worker.configure(self.testenv, self.run_dir, self._run_node, self.rem_host)
103 # some Android UEs only accept new APNs when airplane mode is turned off
104 self.set_airplane_mode(False)
105 self.apn_worker.set_apn()
106 MainLoop.sleep(1)
107 self.set_airplane_mode(True)
108
109 # clear old diag files
110 self._clear_diag_logs()
111
112 def _clear_diag_logs(self):
113 popen_args_clear_diag_logs = \
114 ['su', '-c', '\"rm -r /data/local/tmp/diag_logs/ || true\"']
115 clear_diag_logs_proc = self.run_androidue_cmd('clear-diag-logs', popen_args_clear_diag_logs)
116 clear_diag_logs_proc.launch_sync()
117
118 def verify_metric(self, value, operation='avg', metric='dl_brate', criterion='gt', window=1):
119 self.brate_mon.save_metrics(self.metrics_file)
120 metrics = srsUEMetrics(self.metrics_file)
121 return metrics.verify(value, operation, metric, criterion, window)
122
123 def set_airplane_mode(self, apm_state):
124 self.log("Setting airplane mode: " + str(apm_state))
125 popen_args = ['settings', 'put', 'global', 'airplane_mode_on', str(int(apm_state)), ';',
126 'wait $!;',
127 'su', '-c', '\"am broadcast -a android.intent.action.AIRPLANE_MODE\";']
128 proc = self.run_androidue_cmd('set-airplane-mode', popen_args)
129 proc.launch_sync()
130
131 def get_assigned_addr(self, ipv6=False):
132 ip_prefix = '172.16.0'
133 proc = self.run_androidue_cmd('get-assigned-addr', ['ip', 'addr', 'show'])
134 proc.launch_sync()
135 out_l = proc.get_stdout().split('\n')
136 ip = ''
137 for line in out_l:
138 if ip_prefix in line:
139 ip = line.split(' ')[5][:-3]
140 self.data_interface = line.split(' ')[-1]
141 return ip
142
143########################
144# PUBLIC - INTERNAL API
145########################
146 def cleanup(self):
147 self.set_airplane_mode(True)
148
149 def addr(self):
150 return self._run_node.run_addr()
151
152 def run_node(self):
153 return self._run_node
154
155 def features(self):
156 return self._conf.get('features', [])
157
158###################
159# PUBLIC (test API included)
160###################
161 def run_netns_wait(self, name, popen_args):
162 # This function guarantees the compatibility with the current ping test. Please
163 # note that this function cannot execute commands on the machine the Android UE
164 # is attached to.
165 proc = self.run_androidue_cmd(name, popen_args)
166 proc.launch_sync()
167 return proc
168
169 def connect(self, enb):
170 self.log('Starting AndroidUE')
171 self.run_dir = util.Dir(self.testenv.test().get_run_dir().new_dir(self.name()))
172 self.configure()
173 CONN_CHK = 'osmo-gsm-tester_androidue_conn_chk.sh'
174
175 if 'qc_diag' in self.features():
176 self.qc_diag_mon.start()
177
178 if self._run_node.is_local():
179 popen_args_emm_conn_chk = [CONN_CHK, self._run_node.adb_serial_id(), '0', '0']
180 else:
181 popen_args_emm_conn_chk = [CONN_CHK, '0', self.rem_host.host(), self.rem_host.get_remote_port()]
182
183 # make sure osmo-gsm-tester_androidue_conn_chk.sh is available on the OGT master unit
184 name = 'emm-conn-chk'
185 run_dir = self.run_dir.new_dir(name)
186 emm_conn_chk_proc = process.Process(name, run_dir, popen_args_emm_conn_chk)
187 self.testenv.remember_to_stop(emm_conn_chk_proc)
188 emm_conn_chk_proc.launch()
189
190 # check connection status
191 timer = self.connect_timeout
192 while timer > 0:
193 if timer % self.conn_reset_intvl == 0:
194 self.set_airplane_mode(True)
195 MainLoop.sleep(1)
196 timer -= 1
197 self.set_airplane_mode(False)
198
199 if 'LTE' in emm_conn_chk_proc.get_stdout():
200 if not(self.get_assigned_addr() is ''):
201 self.emm_connected = True
202 self.rrc_connected = True
203 self.testenv.stop_process(emm_conn_chk_proc)
204 break
205
206 MainLoop.sleep(2)
207 timer -= 2
208
209 if timer == 0:
210 raise log.Error('Connection timer of Android UE %s expired' % self._run_node.adb_serial_id())
211
212 self.brate_mon = BitRateMonitor(self.testenv, self.run_dir, self._run_node, self.rem_host, self.data_interface)
213 self.brate_mon.start()
214
215 def is_rrc_connected(self):
216 if not ('qc_diag' in self.features()):
217 raise log.Error('Monitoring RRC states not supported (missing qc_diag feature?)')
218
219 # if not self.qc_diag_mon.running():
220 # raise log.Error('Diag monitoring crashed or was not started')
221
222 rrc_state = self.qc_diag_mon.get_rrc_state()
223 if 'RRC_IDLE_CAMPED' in rrc_state:
224 self.rrc_connected = False
225 elif 'RRC_CONNECTED' in rrc_state:
226 self.rrc_connected = True
227 return self.rrc_connected
228
229 def is_registered(self, mcc_mnc=None):
230 if mcc_mnc:
231 raise log.Error('An AndroidUE cannot register to any predefined MCC/MNC')
232 return self.emm_connected
233
234 def get_counter(self, counter_name):
235 if counter_name == 'prach_sent':
236 # not implemented so far, return 2 to pass tests
237 return 2
238 elif counter_name == 'paging_received':
239 return self.qc_diag_mon.get_paging_counter()
240 else:
241 raise log.Error('Counter %s not implemented' % counter_name)
242
243 def netns(self):
244 return None