• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4 
5 import dbus
6 import dbus.service
7 import logging
8 
9 import dbus_std_ifaces
10 import pm_constants
11 import pm_errors
12 import utils
13 
14 from autotest_lib.client.cros.cellular import mm1_constants
15 
16 class IncorrectPasswordError(pm_errors.MMMobileEquipmentError):
17     """ Wrapper around MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD. """
18 
19     def __init__(self):
20         pm_errors.MMMobileEquipmentError.__init__(
21                 self, pm_errors.MMMobileEquipmentError.INCORRECT_PASSWORD,
22                 'Incorrect password')
23 
24 class SimPukError(pm_errors.MMMobileEquipmentError):
25     """ Wrapper around MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK. """
26 
27     def __init__(self):
28         pm_errors.MMMobileEquipmentError.__init__(
29                 self, pm_errors.MMMobileEquipmentError.SIM_PUK,
30                 'SIM PUK required')
31 
32 class SimFailureError(pm_errors.MMMobileEquipmentError):
33     """ Wrapper around MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE. """
34 
35     def __init__(self):
36         pm_errors.MMMobileEquipmentError.__init__(
37                 self, pm_errors.MMMobileEquipmentError.SIM_FAILURE,
38                 'SIM failure')
39 
40 class SIM(dbus_std_ifaces.DBusProperties):
41     """
42     Pseudomodem implementation of the org.freedesktop.ModemManager1.Sim
43     interface.
44 
45     Broadband modems usually need a SIM card to operate. Each Modem object will
46     therefore expose up to one SIM object, which allows SIM-specific actions
47     such as PIN unlocking.
48 
49     The SIM interface handles communication with SIM, USIM, and RUIM (CDMA SIM)
50     cards.
51 
52     """
53 
54     # Multiple object paths needs to be supported so that the SIM can be
55     # "reset". This allows the object to reappear on a new path as if it has
56     # been reset.
57     SUPPORTS_MULTIPLE_OBJECT_PATHS = True
58 
59     DEFAULT_MSIN = '1234567890'
60     DEFAULT_IMSI = '888999111'
61     DEFAULT_PIN = '1111'
62     DEFAULT_PUK = '12345678'
63     DEFAULT_PIN_RETRIES = 3
64     DEFAULT_PUK_RETRIES = 10
65 
66     class Carrier:
67         """
68         Represents a 3GPP carrier that can be stored by a SIM object.
69 
70         """
71         MCC_LIST = {
72             'test' : '001',
73             'us': '310',
74             'de': '262',
75             'es': '214',
76             'fr': '208',
77             'gb': '234',
78             'it': '222',
79             'nl': '204'
80         }
81 
82         CARRIER_LIST = {
83             'test' : ('test', '000', pm_constants.DEFAULT_TEST_NETWORK_PREFIX),
84             'banana' : ('us', '001', 'Banana-Comm'),
85             'att': ('us', '090', 'AT&T'),
86             'tmobile': ('us', '026', 'T-Mobile'),
87             'simyo': ('de', '03', 'simyo'),
88             'movistar': ('es', '07', 'Movistar'),
89             'sfr': ('fr', '10', 'SFR'),
90             'three': ('gb', '20', '3'),
91             'threeita': ('it', '99', '3ITA'),
92             'kpn': ('nl', '08', 'KPN')
93         }
94 
95         def __init__(self, carrier='test'):
96            carrier = self.CARRIER_LIST.get(carrier, self.CARRIER_LIST['test'])
97 
98            self.mcc = self.MCC_LIST[carrier[0]]
99            self.mnc = carrier[1]
100            self.operator_name = carrier[2]
101            if self.operator_name != 'Banana-Comm':
102               self.operator_name = self.operator_name + ' - Fake'
103            self.operator_id = self.mcc + self.mnc
104 
105 
106     def __init__(self,
107                  carrier,
108                  access_technology,
109                  index=0,
110                  pin=DEFAULT_PIN,
111                  puk=DEFAULT_PUK,
112                  pin_retries=DEFAULT_PIN_RETRIES,
113                  puk_retries=DEFAULT_PUK_RETRIES,
114                  locked=False,
115                  msin=DEFAULT_MSIN,
116                  imsi=DEFAULT_IMSI,
117                  config=None):
118         if not carrier:
119             raise TypeError('A carrier is required.')
120         path = mm1_constants.MM1 + '/SIM/' + str(index)
121         self.msin = msin
122         self._carrier = carrier
123         self.imsi = carrier.operator_id + imsi
124         self._index = 0
125         self._total_pin_retries = pin_retries
126         self._total_puk_retries = puk_retries
127         self._lock_data = {
128             mm1_constants.MM_MODEM_LOCK_SIM_PIN : {
129                 'code' : pin,
130                 'retries' : pin_retries
131             },
132             mm1_constants.MM_MODEM_LOCK_SIM_PUK : {
133                 'code' : puk,
134                 'retries' : puk_retries
135             }
136         }
137         self._lock_enabled = locked
138         self._show_retries = locked
139         if locked:
140             self._lock_type = mm1_constants.MM_MODEM_LOCK_SIM_PIN
141         else:
142             self._lock_type = mm1_constants.MM_MODEM_LOCK_NONE
143         self._modem = None
144         self.access_technology = access_technology
145         dbus_std_ifaces.DBusProperties.__init__(self, path, None, config)
146 
147 
148     def IncrementPath(self):
149         """
150         Increments the current index at which this modem is exposed on DBus.
151         E.g. if the current path is org/freedesktop/ModemManager/Modem/0, the
152         path will change to org/freedesktop/ModemManager/Modem/1.
153 
154         Calling this method does not remove the object from its current path,
155         which means that it will be available via both the old and the new
156         paths. This is currently only used by Reset, in conjunction with
157         dbus_std_ifaces.DBusObjectManager.[Add|Remove].
158 
159         """
160         self._index += 1
161         path = mm1_constants.MM1 + '/SIM/' + str(self._index)
162         logging.info('SIM coming back as: ' + path)
163         self.SetPath(path)
164 
165 
166     def Reset(self):
167         """ Resets the SIM. This will lock the SIM if locks are enabled. """
168         self.IncrementPath()
169         if not self.locked and self._lock_enabled:
170             self._lock_type = mm1_constants.MM_MODEM_LOCK_SIM_PIN
171 
172 
173     @property
174     def lock_type(self):
175         """
176         Returns the current lock type of the SIM. Can be used to determine
177         whether or not the SIM is locked.
178 
179         @returns: The lock type, as a MMModemLock value.
180 
181         """
182         return self._lock_type
183 
184 
185     @property
186     def unlock_retries(self):
187         """
188         Returns the number of unlock retries left.
189 
190         @returns: The number of unlock retries for each lock type the SIM
191                 supports as a dictionary.
192 
193         """
194         retries = dbus.Dictionary(signature='uu')
195         if not self._show_retries:
196             return retries
197         for k, v in self._lock_data.iteritems():
198             retries[dbus.types.UInt32(k)] = dbus.types.UInt32(v['retries'])
199         return retries
200 
201 
202     @property
203     def enabled_locks(self):
204         """
205         Returns the currently enabled facility locks.
206 
207         @returns: The currently enabled facility locks, as a MMModem3gppFacility
208                 value.
209 
210         """
211         if self._lock_enabled:
212             return mm1_constants.MM_MODEM_3GPP_FACILITY_SIM
213         return mm1_constants.MM_MODEM_3GPP_FACILITY_NONE
214 
215 
216     @property
217     def locked(self):
218         """ @returns: True, if the SIM is locked. False, otherwise. """
219         return not (self._lock_type == mm1_constants.MM_MODEM_LOCK_NONE or
220             self._lock_type == mm1_constants.MM_MODEM_LOCK_UNKNOWN)
221 
222 
223     @property
224     def modem(self):
225         """
226         @returns: the modem object that this SIM is currently plugged into.
227 
228         """
229         return self._modem
230 
231 
232     @modem.setter
233     def modem(self, modem):
234         """
235         Assigns a modem object to this SIM, so that the modem knows about it.
236         This should only be called directly by a modem object.
237 
238         @param modem: The modem to be associated with this SIM.
239 
240         """
241         self._modem = modem
242 
243 
244     @property
245     def carrier(self):
246         """
247         @returns: An instance of SIM.Carrier that contains the carrier
248                 information assigned to this SIM.
249 
250         """
251         return self._carrier
252 
253 
254     def _DBusPropertiesDict(self):
255         imsi = self.imsi
256         if self.locked:
257             msin = ''
258             op_id = ''
259             op_name = ''
260         else:
261             msin = self.msin
262             op_id = self._carrier.operator_id
263             op_name = self._carrier.operator_name
264         return {
265             'SimIdentifier' : msin,
266             'Imsi' : imsi,
267             'OperatorIdentifier' : op_id,
268             'OperatorName' : op_name
269         }
270 
271 
272     def _InitializeProperties(self):
273         return { mm1_constants.I_SIM : self._DBusPropertiesDict() }
274 
275 
276     def _UpdateProperties(self):
277         self.SetAll(mm1_constants.I_SIM, self._DBusPropertiesDict())
278 
279 
280     def _CheckCode(self, code, lock_data, next_lock, error_to_raise):
281         # Checks |code| against |lock_data['code']|. If the codes don't match:
282         #
283         #   - if the number of retries left for |lock_data| drops down to 0,
284         #     the current lock type gets set to |next_lock| and
285         #     |error_to_raise| is raised.
286         #
287         #   - otherwise, IncorrectPasswordError is raised.
288         #
289         # If the codes match, no error is raised.
290 
291         if code == lock_data['code']:
292             # Codes match, nothing to do.
293             return
294 
295         # Codes didn't match. Figure out which error to raise based on
296         # remaining retries.
297         lock_data['retries'] -= 1
298         self._show_retries = True
299         if lock_data['retries'] == 0:
300             logging.info('Retries exceeded the allowed number.')
301             if next_lock:
302                 self._lock_type = next_lock
303                 self._lock_enabled = True
304         else:
305             error_to_raise = IncorrectPasswordError()
306         self._modem.UpdateLockStatus()
307         raise error_to_raise
308 
309 
310     def _ResetRetries(self, lock_type):
311         if lock_type == mm1_constants.MM_MODEM_LOCK_SIM_PIN:
312             value = self._total_pin_retries
313         elif lock_type == mm1_constants.MM_MODEM_LOCK_SIM_PUK:
314             value = self._total_puk_retries
315         else:
316             raise TypeError('Invalid SIM lock type')
317         self._lock_data[lock_type]['retries'] = value
318 
319 
320     @utils.log_dbus_method()
321     @dbus.service.method(mm1_constants.I_SIM, in_signature='s')
322     def SendPin(self, pin):
323         """
324         Sends the PIN to unlock the SIM card.
325 
326         @param pin: A string containing the PIN code.
327 
328         """
329         if not self.locked:
330             logging.info('SIM is not locked. Nothing to do.')
331             return
332 
333         if self._lock_type == mm1_constants.MM_MODEM_LOCK_SIM_PUK:
334             if self._lock_data[self._lock_type]['retries'] == 0:
335                 raise SimFailureError()
336             else:
337                 raise SimPukError()
338 
339         lock_data = self._lock_data.get(self._lock_type, None)
340         if not lock_data:
341             raise pm_errors.MMCoreError(
342                 pm_errors.MMCoreError.FAILED,
343                 'Current lock type does not match the SIM lock capabilities.')
344 
345         self._CheckCode(pin, lock_data, mm1_constants.MM_MODEM_LOCK_SIM_PUK,
346                         SimPukError())
347 
348         logging.info('Entered correct PIN.')
349         self._ResetRetries(mm1_constants.MM_MODEM_LOCK_SIM_PIN)
350         self._lock_type = mm1_constants.MM_MODEM_LOCK_NONE
351         self._modem.UpdateLockStatus()
352         self._modem.Expose3GPPProperties()
353         self._UpdateProperties()
354 
355 
356     @utils.log_dbus_method()
357     @dbus.service.method(mm1_constants.I_SIM, in_signature='ss')
358     def SendPuk(self, puk, pin):
359         """
360         Sends the PUK and a new PIN to unlock the SIM card.
361 
362         @param puk: A string containing the PUK code.
363         @param pin: A string containing the PIN code.
364 
365         """
366         if self._lock_type != mm1_constants.MM_MODEM_LOCK_SIM_PUK:
367             logging.info('No PUK lock in place. Nothing to do.')
368             return
369 
370         lock_data = self._lock_data.get(self._lock_type, None)
371         if not lock_data:
372             raise pm_errors.MMCoreError(
373                     pm_errors.MMCoreError.FAILED,
374                     'Current lock type does not match the SIM locks in place.')
375 
376         if lock_data['retries'] == 0:
377             raise SimFailureError()
378 
379         self._CheckCode(puk, lock_data, None, SimFailureError())
380 
381         logging.info('Entered correct PUK.')
382         self._ResetRetries(mm1_constants.MM_MODEM_LOCK_SIM_PIN)
383         self._ResetRetries(mm1_constants.MM_MODEM_LOCK_SIM_PUK)
384         self._lock_data[mm1_constants.MM_MODEM_LOCK_SIM_PIN]['code'] = pin
385         self._lock_type = mm1_constants.MM_MODEM_LOCK_NONE
386         self._modem.UpdateLockStatus()
387         self._modem.Expose3GPPProperties()
388         self._UpdateProperties()
389 
390 
391     @utils.log_dbus_method()
392     @dbus.service.method(mm1_constants.I_SIM, in_signature='sb')
393     def EnablePin(self, pin, enabled):
394         """
395         Enables or disables PIN checking.
396 
397         @param pin: A string containing the PIN code.
398         @param enabled: True to enable PIN, False otherwise.
399 
400         """
401         if enabled:
402             self._EnablePin(pin)
403         else:
404             self._DisablePin(pin)
405 
406 
407     def _EnablePin(self, pin):
408         # Operation fails if the SIM is locked or PIN lock is already
409         # enabled.
410         if self.locked or self._lock_enabled:
411             raise SimFailureError()
412 
413         lock_data = self._lock_data[mm1_constants.MM_MODEM_LOCK_SIM_PIN]
414         self._CheckCode(pin, lock_data, mm1_constants.MM_MODEM_LOCK_SIM_PUK,
415                         SimPukError())
416         self._lock_enabled = True
417         self._show_retries = True
418         self._ResetRetries(mm1_constants.MM_MODEM_LOCK_SIM_PIN)
419         self._UpdateProperties()
420         self.modem.UpdateLockStatus()
421 
422 
423     def _DisablePin(self, pin):
424         if not self._lock_enabled:
425             raise SimFailureError()
426 
427         if self.locked:
428             self.SendPin(pin)
429         else:
430             lock_data = self._lock_data[mm1_constants.MM_MODEM_LOCK_SIM_PIN]
431             self._CheckCode(pin, lock_data,
432                             mm1_constants.MM_MODEM_LOCK_SIM_PUK, SimPukError())
433             self._ResetRetries(mm1_constants.MM_MODEM_LOCK_SIM_PIN)
434         self._lock_enabled = False
435         self._UpdateProperties()
436         self.modem.UpdateLockStatus()
437 
438 
439     @utils.log_dbus_method()
440     @dbus.service.method(mm1_constants.I_SIM, in_signature='ss')
441     def ChangePin(self, old_pin, new_pin):
442         """
443         Changes the PIN code.
444 
445         @param old_pin: A string containing the old PIN code.
446         @param new_pin: A string containing the new PIN code.
447 
448         """
449         if not self._lock_enabled or self.locked:
450             raise SimFailureError()
451 
452         lock_data = self._lock_data[mm1_constants.MM_MODEM_LOCK_SIM_PIN]
453         self._CheckCode(old_pin, lock_data,
454                         mm1_constants.MM_MODEM_LOCK_SIM_PUK, SimPukError())
455         self._ResetRetries(mm1_constants.MM_MODEM_LOCK_SIM_PIN)
456         self._lock_data[mm1_constants.MM_MODEM_LOCK_SIM_PIN]['code'] = new_pin
457         self._UpdateProperties()
458         self.modem.UpdateLockStatus()
459