[meego-commits] 10774: Changes to Trunk/ofono

Rolla Selbak no_reply at build.meego.com
Thu Dec 9 16:44:31 UTC 2010


Hi,
I have made the following changes to ofono in project Trunk. Please review and accept ASAP.

Thank You,
Rolla Selbak

[This message was auto-generated]

---

Request #10774:

  submit:   Trunk:Testing/ofono(r14) -> Trunk/ofono


Message:
    Trunk promotion

State:   new          2010-12-09T08:44:30 rolla
Comment: None



changes files:
--------------
--- ofono.changes
+++ ofono.changes
@@ -0,0 +1,19 @@
+* Wed Dec 08 2010 Martin Xu <martin.xu at intel.com> - 0.36
+- upgrade to 0.36
+-  Fix issue with CLIR Invocation and Suppression.
+-  Fix issue with power/online transition with ZTE devices.
+-  Fix segmentation fault when removing Nokia Datacard.
+-  Add support for Nokia CS-17 dongles.
+-  Add support for Ericsson F5521gw devices.
+-  Add support for CAIF network interface management.
+-  Add support for COLR in generic AT modem driver.
+-  Add support for SMS Point-to-Point download to UICC.
+-  Add support for checking specific service availability.
+-  Add support for handling null text field for STK.
+
+* Mon Nov 15 2010 Martin Xu <martin.xu at intel.com> - 0.35
+- upgrade to 0.35
+
+* Wed Nov 03 2010 Martin Xu <martin.xu at intel.com> - 0.34
+- upgrade to 0.34
+

old:
----
  ofono-0.33.tar.gz

new:
----
  ofono-0.36.tar.gz

spec files:
-----------
--- ofono.spec
+++ ofono.spec
@@ -7,7 +7,7 @@
 
 Name:       ofono
 Summary:    Open Source Telephony
-Version:    0.33
+Version:    0.36
 Release:    1
 Group:      System/Networking
 License:    GPLv2
@@ -87,9 +87,9 @@
 # >> files
 %doc COPYING ChangeLog AUTHORS README
 %config(noreplace) %{_sysconfdir}/dbus-1/system.d/*.conf
-%config(noreplace) %{_sysconfdir}/%{name}/modem.conf
 %{_sbindir}/*
 /lib/udev/rules.d/*
+/etc/ofono/phonesim.conf
 %doc /usr/share/man/man8/ofonod.8.gz
 # << files
 

other changes:
--------------

++++++ ofono-0.33.tar.gz -> ofono-0.36.tar.gz
--- AUTHORS
+++ AUTHORS
@@ -37,3 +37,7 @@
 Jeevaka Badrappan <jeevaka.badrappan at elektrobit.com>
 Frank Gau <fgau at gau-net.de>
 Kai Vehmanen <kai.vehmanen at nokia.com>
+Mika Liljeberg <mika.liljeberg at nokia.com>
+Marit Henriksen <marit.henriksen at stericsson.com>
+Guillaume Lucas <guillaumex.lucas at intel.com>
+George Matveev <george at matveev.se>
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,41 @@
+ver 0.36:
+	Fix issue with CLIR Invocation and Suppression.
+	Fix issue with power/online transition with ZTE devices.
+	Fix segmentation fault when removing Nokia Datacard.
+	Add support for Nokia CS-17 dongles.
+	Add support for Ericsson F5521gw devices.
+	Add support for CAIF network interface management.
+	Add support for COLR in generic AT modem driver.
+	Add support for SMS Point-to-Point download to UICC.
+	Add support for checking specific service availability.
+	Add support for handling null text field for STK.
+
+ver 0.35:
+	Fix issue with FDN and BDN enabled checks.
+	Fix issue with capabilities and Phonet support.
+	Fix issue with timeout for ISI network deregistration.
+	Add support for Push Notification interface.
+	Add support for Smart Messaging interface.
+	Remove generic AT command modem plugin.
+
+ver 0.34:
+	Fix issue with sim_fs_op_error handling.
+	Fix issue with not handling GPRS context driver failures.
+	Add support for multiple GPRS context activations.
+	Add support for deactivating all GPRS contexts.
+	Add support for configuring MMS context settings.
+	Add support for barred dialing indication property.
+	Add support for fast dormancy settings property.
+	Add support for handling Play Tone proactive command.
+	Add support for indicating handled STK proactive commands.
+	Add support for two active GPRS contexts with MBM modems.
+	Add support for time zone reporting with Ericsson MBM modems.
+	Add support for detecting IFX modems stuck in multiplexer mode.
+	Add support for IFX using up to three active GPRS contexts.
+	Add support for IFX device shutdown when DLC disconnects.
+	Add support for Phonesim specific configuration files.
+	Remove deprecated modem.conf support.
+
 ver 0.33:
 	Fix wrong string to enum mapping of radio settings.
 	Fix issue with MMI code to bearer class mappings.
--- Makefile.am
+++ Makefile.am
@@ -94,13 +94,6 @@
 endif
 endif
 
-builtin_modules += modemconf
-builtin_sources += plugins/modemconf.c
-
-if DATAFILES
-conf_DATA += plugins/modem.conf
-endif
-
 if ISIMODEM
 builtin_modules += isimodem
 builtin_sources += $(gisi_sources) \
@@ -233,15 +226,20 @@
 			drivers/stemodem/stemodem.c \
 			drivers/stemodem/voicecall.c \
 			drivers/stemodem/radio-settings.c \
+			drivers/stemodem/caif_rtnl.c \
+			drivers/stemodem/caif_rtnl.h \
 			drivers/stemodem/gprs-context.c \
 			drivers/stemodem/caif_socket.h \
 			drivers/stemodem/if_caif.h
 
+if PHONESIM
 builtin_modules += phonesim
 builtin_sources += plugins/phonesim.c
 
-builtin_modules += atgen
-builtin_sources += plugins/atgen.c
+if DATAFILES
+conf_DATA += plugins/phonesim.conf
+endif
+endif
 
 builtin_modules += g1
 builtin_sources += plugins/g1.c
@@ -300,6 +298,9 @@
 builtin_modules += smart_messaging
 builtin_sources += plugins/smart-messaging.c
 
+builtin_modules += push_notification
+builtin_sources += plugins/push-notification.c
+
 sbin_PROGRAMS = src/ofonod
 
 src_ofonod_SOURCES = $(gdbus_sources) $(builtin_sources) src/ofono.ver \
@@ -316,7 +317,8 @@
 			src/gprs.c src/idmap.h src/idmap.c \
 			src/radio-settings.c src/stkutil.h src/stkutil.c \
 			src/nettime.c src/stkagent.c src/stkagent.h \
-			src/simfs.c src/simfs.h src/audio-settings.c
+			src/simfs.c src/simfs.h src/audio-settings.c \
+			src/smsagent.c src/smsagent.h
 
 src_ofonod_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@ -ldl
 
@@ -357,9 +359,11 @@
 
 
 test_scripts = test/backtrace \
-		test/create-context \
+		test/create-internet-context \
+		test/create-mms-context \
 		test/activate-context \
 		test/deactivate-context \
+		test/deactivate-all \
 		test/dial-number \
 		test/list-calls \
 		test/answer-calls \
@@ -368,10 +372,11 @@
 		test/disable-modem \
 		test/enable-modem \
 		test/enter-pin \
+		test/reset-pin \
 		test/hangup-all \
 		test/hangup-active \
+		test/set-mms-details \
 		test/set-roaming-allowed \
-		test/set-context \
 		test/list-contexts \
 		test/list-modems \
 		test/list-operators \
@@ -410,14 +415,18 @@
 		test/unlock-pin \
 		test/enable-gprs \
 		test/disable-gprs \
-		test/get-icon
+		test/get-icon \
+		test/set-fast-dormancy \
+		test/test-push-notification \
+		test/test-smart-messaging \
+		test/send-vcard
 
 if TEST
 testdir = $(pkglibdir)/test
 test_SCRIPTS = $(test_scripts)
 endif
 
-conf_files = src/ofono.conf plugins/modem.conf
+conf_files = src/ofono.conf plugins/phonesim.conf
 
 EXTRA_DIST = src/genbuiltin $(conf_files) $(udev_files) \
 					$(doc_files) $(test_scripts)
--- Makefile.in
+++ Makefile.in
@@ -73,10 +73,7 @@
 @ISIMODEM_TRUE@	plugins/isigen.c plugins/n900.c \
 @ISIMODEM_TRUE@	plugins/nokia-gpio.h plugins/nokia-gpio.c
 @ATMODEM_TRUE at am__append_7 = atmodem nwmodem huaweimodem calypsomodem \
- at ATMODEM_TRUE@	hfpmodem mbmmodem hsomodem ifxmodem stemodem \
- at ATMODEM_TRUE@	phonesim atgen g1 wavecom calypso mbm hso zte \
- at ATMODEM_TRUE@	huawei novatel nokia bluetooth hfp palmpre ifx \
- at ATMODEM_TRUE@	ste caif
+ at ATMODEM_TRUE@	hfpmodem mbmmodem hsomodem ifxmodem stemodem
 @ATMODEM_TRUE at am__append_8 = $(gatchat_sources) \
 @ATMODEM_TRUE@	drivers/atmodem/atmodem.h \
 @ATMODEM_TRUE@	drivers/atmodem/atmodem.c \
@@ -140,18 +137,25 @@
 @ATMODEM_TRUE@	drivers/stemodem/stemodem.c \
 @ATMODEM_TRUE@	drivers/stemodem/voicecall.c \
 @ATMODEM_TRUE@	drivers/stemodem/radio-settings.c \
+ at ATMODEM_TRUE@	drivers/stemodem/caif_rtnl.c \
+ at ATMODEM_TRUE@	drivers/stemodem/caif_rtnl.h \
 @ATMODEM_TRUE@	drivers/stemodem/gprs-context.c \
 @ATMODEM_TRUE@	drivers/stemodem/caif_socket.h \
- at ATMODEM_TRUE@	drivers/stemodem/if_caif.h plugins/phonesim.c \
- at ATMODEM_TRUE@	plugins/atgen.c plugins/g1.c plugins/wavecom.c \
+ at ATMODEM_TRUE@	drivers/stemodem/if_caif.h
+ at ATMODEM_TRUE@@PHONESIM_TRUE at am__append_9 = phonesim
+ at ATMODEM_TRUE@@PHONESIM_TRUE at am__append_10 = plugins/phonesim.c
+ at ATMODEM_TRUE@@DATAFILES_TRUE@@PHONESIM_TRUE at am__append_11 = plugins/phonesim.conf
+ at ATMODEM_TRUE@am__append_12 = g1 wavecom calypso mbm hso zte huawei \
+ at ATMODEM_TRUE@	novatel nokia bluetooth hfp palmpre ifx ste caif
+ at ATMODEM_TRUE@am__append_13 = plugins/g1.c plugins/wavecom.c \
 @ATMODEM_TRUE@	plugins/calypso.c plugins/mbm.c plugins/hso.c \
 @ATMODEM_TRUE@	plugins/zte.c plugins/huawei.c plugins/novatel.c \
 @ATMODEM_TRUE@	plugins/nokia.c plugins/bluetooth.c \
 @ATMODEM_TRUE@	plugins/bluetooth.h plugins/hfp.c \
 @ATMODEM_TRUE@	plugins/bluetooth.h plugins/palmpre.c \
 @ATMODEM_TRUE@	plugins/ifx.c plugins/ste.c plugins/caif.c
- at MAINTAINER_MODE_TRUE@am__append_9 = example_history example_nettime
- at MAINTAINER_MODE_TRUE@am__append_10 = examples/history.c \
+ at MAINTAINER_MODE_TRUE@am__append_14 = example_history example_nettime
+ at MAINTAINER_MODE_TRUE@am__append_15 = examples/history.c \
 @MAINTAINER_MODE_TRUE@	examples/nettime.c
 sbin_PROGRAMS = src/ofonod$(EXEEXT)
 noinst_PROGRAMS = unit/test-common$(EXEEXT) unit/test-util$(EXEEXT) \
@@ -213,11 +217,11 @@
 gatchat_test_server_DEPENDENCIES =
 am__src_ofonod_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \
 	gdbus/watch.c gdbus/object.c gdbus/polkit.c plugins/udev.c \
-	plugins/modemconf.c gisi/modem.h gisi/modem.c gisi/netlink.h \
-	gisi/netlink.c gisi/socket.h gisi/socket.c gisi/client.h \
-	gisi/client.c gisi/server.h gisi/server.c gisi/pep.h \
-	gisi/pep.c gisi/pipe.h gisi/pipe.c gisi/iter.h gisi/iter.c \
-	gisi/verify.c gisi/phonet.h drivers/isimodem/isimodem.h \
+	gisi/modem.h gisi/modem.c gisi/netlink.h gisi/netlink.c \
+	gisi/socket.h gisi/socket.c gisi/client.h gisi/client.c \
+	gisi/server.h gisi/server.c gisi/pep.h gisi/pep.c gisi/pipe.h \
+	gisi/pipe.c gisi/iter.h gisi/iter.c gisi/verify.c \
+	gisi/phonet.h drivers/isimodem/isimodem.h \
 	drivers/isimodem/isimodem.c drivers/isimodem/mtc.h \
 	drivers/isimodem/debug.h drivers/isimodem/isiutil.h \
 	drivers/isimodem/debug.c drivers/isimodem/phonebook.c \
@@ -286,25 +290,27 @@
 	drivers/ifxmodem/gprs-context.c drivers/ifxmodem/stk.c \
 	drivers/stemodem/stemodem.h drivers/stemodem/stemodem.c \
 	drivers/stemodem/voicecall.c drivers/stemodem/radio-settings.c \
+	drivers/stemodem/caif_rtnl.c drivers/stemodem/caif_rtnl.h \
 	drivers/stemodem/gprs-context.c drivers/stemodem/caif_socket.h \
-	drivers/stemodem/if_caif.h plugins/phonesim.c plugins/atgen.c \
-	plugins/g1.c plugins/wavecom.c plugins/calypso.c plugins/mbm.c \
+	drivers/stemodem/if_caif.h plugins/phonesim.c plugins/g1.c \
+	plugins/wavecom.c plugins/calypso.c plugins/mbm.c \
 	plugins/hso.c plugins/zte.c plugins/huawei.c plugins/novatel.c \
 	plugins/nokia.c plugins/bluetooth.c plugins/bluetooth.h \
 	plugins/hfp.c plugins/palmpre.c plugins/ifx.c plugins/ste.c \
 	plugins/caif.c examples/history.c examples/nettime.c \
-	plugins/smart-messaging.c src/ofono.ver src/main.c src/ofono.h \
-	src/log.c src/plugin.c src/modem.c src/common.h src/common.c \
-	src/manager.c src/dbus.c src/util.h src/util.c src/network.c \
-	src/voicecall.c src/ussd.c src/sms.c src/call-settings.c \
-	src/call-forwarding.c src/call-meter.c src/smsutil.h \
-	src/smsutil.c src/ssn.c src/call-barring.c src/sim.c src/stk.c \
-	src/phonebook.c src/history.c src/message-waiting.c \
-	src/simutil.h src/simutil.c src/storage.h src/storage.c \
-	src/cbs.c src/watch.c src/call-volume.c src/gprs.c src/idmap.h \
+	plugins/smart-messaging.c plugins/push-notification.c \
+	src/ofono.ver src/main.c src/ofono.h src/log.c src/plugin.c \
+	src/modem.c src/common.h src/common.c src/manager.c src/dbus.c \
+	src/util.h src/util.c src/network.c src/voicecall.c src/ussd.c \
+	src/sms.c src/call-settings.c src/call-forwarding.c \
+	src/call-meter.c src/smsutil.h src/smsutil.c src/ssn.c \
+	src/call-barring.c src/sim.c src/stk.c src/phonebook.c \
+	src/history.c src/message-waiting.c src/simutil.h \
+	src/simutil.c src/storage.h src/storage.c src/cbs.c \
+	src/watch.c src/call-volume.c src/gprs.c src/idmap.h \
 	src/idmap.c src/radio-settings.c src/stkutil.h src/stkutil.c \
 	src/nettime.c src/stkagent.c src/stkagent.h src/simfs.c \
-	src/simfs.h src/audio-settings.c
+	src/simfs.h src/audio-settings.c src/smsagent.c src/smsagent.h
 am__objects_2 = gdbus/mainloop.$(OBJEXT) gdbus/watch.$(OBJEXT) \
 	gdbus/object.$(OBJEXT) gdbus/polkit.$(OBJEXT)
 @UDEV_TRUE at am__objects_3 = plugins/udev.$(OBJEXT)
@@ -384,9 +390,11 @@
 @ATMODEM_TRUE@	drivers/stemodem/stemodem.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/stemodem/voicecall.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/stemodem/radio-settings.$(OBJEXT) \
- at ATMODEM_TRUE@	drivers/stemodem/gprs-context.$(OBJEXT) \
- at ATMODEM_TRUE@	plugins/phonesim.$(OBJEXT) \
- at ATMODEM_TRUE@	plugins/atgen.$(OBJEXT) plugins/g1.$(OBJEXT) \
+ at ATMODEM_TRUE@	drivers/stemodem/caif_rtnl.$(OBJEXT) \
+ at ATMODEM_TRUE@	drivers/stemodem/gprs-context.$(OBJEXT)
+ at ATMODEM_TRUE@@PHONESIM_TRUE at am__objects_7 =  \
+ at ATMODEM_TRUE@@PHONESIM_TRUE@	plugins/phonesim.$(OBJEXT)
+ at ATMODEM_TRUE@am__objects_8 = plugins/g1.$(OBJEXT) \
 @ATMODEM_TRUE@	plugins/wavecom.$(OBJEXT) \
 @ATMODEM_TRUE@	plugins/calypso.$(OBJEXT) plugins/mbm.$(OBJEXT) \
 @ATMODEM_TRUE@	plugins/hso.$(OBJEXT) plugins/zte.$(OBJEXT) \
@@ -397,12 +405,13 @@
 @ATMODEM_TRUE@	plugins/hfp.$(OBJEXT) plugins/palmpre.$(OBJEXT) \
 @ATMODEM_TRUE@	plugins/ifx.$(OBJEXT) plugins/ste.$(OBJEXT) \
 @ATMODEM_TRUE@	plugins/caif.$(OBJEXT)
- at MAINTAINER_MODE_TRUE@am__objects_7 = examples/history.$(OBJEXT) \
+ at MAINTAINER_MODE_TRUE@am__objects_9 = examples/history.$(OBJEXT) \
 @MAINTAINER_MODE_TRUE@	examples/nettime.$(OBJEXT)
-am__objects_8 = $(am__objects_3) plugins/modemconf.$(OBJEXT) \
-	$(am__objects_5) $(am__objects_6) $(am__objects_7) \
-	plugins/smart-messaging.$(OBJEXT)
-am_src_ofonod_OBJECTS = $(am__objects_2) $(am__objects_8) \
+am__objects_10 = $(am__objects_3) $(am__objects_5) $(am__objects_6) \
+	$(am__objects_7) $(am__objects_8) $(am__objects_9) \
+	plugins/smart-messaging.$(OBJEXT) \
+	plugins/push-notification.$(OBJEXT)
+am_src_ofonod_OBJECTS = $(am__objects_2) $(am__objects_10) \
 	src/main.$(OBJEXT) src/log.$(OBJEXT) src/plugin.$(OBJEXT) \
 	src/modem.$(OBJEXT) src/common.$(OBJEXT) src/manager.$(OBJEXT) \
 	src/dbus.$(OBJEXT) src/util.$(OBJEXT) src/network.$(OBJEXT) \
@@ -417,7 +426,8 @@
 	src/gprs.$(OBJEXT) src/idmap.$(OBJEXT) \
 	src/radio-settings.$(OBJEXT) src/stkutil.$(OBJEXT) \
 	src/nettime.$(OBJEXT) src/stkagent.$(OBJEXT) \
-	src/simfs.$(OBJEXT) src/audio-settings.$(OBJEXT)
+	src/simfs.$(OBJEXT) src/audio-settings.$(OBJEXT) \
+	src/smsagent.$(OBJEXT)
 src_ofonod_OBJECTS = $(am_src_ofonod_OBJECTS)
 am__DEPENDENCIES_1 =
 am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
@@ -693,13 +703,15 @@
 @DATAFILES_TRUE@@SYSTEMD_TRUE at systemdunitdir = @SYSTEMD_UNITDIR@
 @DATAFILES_TRUE@@SYSTEMD_TRUE at systemdunit_DATA = src/ofono.service
 @DATAFILES_TRUE at confdir = $(sysconfdir)/ofono
- at DATAFILES_TRUE@conf_DATA = plugins/modem.conf
+ at DATAFILES_TRUE@conf_DATA = $(am__append_11)
 @DATAFILES_TRUE at statedir = $(localstatedir)/lib/ofono
 @DATAFILES_TRUE at state_DATA = 
-builtin_modules = $(am__append_1) modemconf $(am__append_5) \
-	$(am__append_7) $(am__append_9) smart_messaging
-builtin_sources = $(am__append_2) plugins/modemconf.c $(am__append_6) \
-	$(am__append_8) $(am__append_10) plugins/smart-messaging.c
+builtin_modules = $(am__append_1) $(am__append_5) $(am__append_7) \
+	$(am__append_9) $(am__append_12) $(am__append_14) \
+	smart_messaging push_notification
+builtin_sources = $(am__append_2) $(am__append_6) $(am__append_8) \
+	$(am__append_10) $(am__append_13) $(am__append_15) \
+	plugins/smart-messaging.c plugins/push-notification.c
 builtin_libadd = $(am__append_4)
 builtin_cflags = $(am__append_3)
 gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
@@ -752,7 +764,8 @@
 			src/gprs.c src/idmap.h src/idmap.c \
 			src/radio-settings.c src/stkutil.h src/stkutil.c \
 			src/nettime.c src/stkagent.c src/stkagent.h \
-			src/simfs.c src/simfs.h src/audio-settings.c
+			src/simfs.c src/simfs.h src/audio-settings.c \
+			src/smsagent.c src/smsagent.h
 
 src_ofonod_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@ -ldl
 src_ofonod_LDFLAGS = -Wl,--export-dynamic \
@@ -785,9 +798,11 @@
 			doc/audio-settings-api.txt
 
 test_scripts = test/backtrace \
-		test/create-context \
+		test/create-internet-context \
+		test/create-mms-context \
 		test/activate-context \
 		test/deactivate-context \
+		test/deactivate-all \
 		test/dial-number \
 		test/list-calls \
 		test/answer-calls \
@@ -796,10 +811,11 @@
 		test/disable-modem \
 		test/enable-modem \
 		test/enter-pin \
+		test/reset-pin \
 		test/hangup-all \
 		test/hangup-active \
+		test/set-mms-details \
 		test/set-roaming-allowed \
-		test/set-context \
 		test/list-contexts \
 		test/list-modems \
 		test/list-operators \
@@ -838,11 +854,15 @@
 		test/unlock-pin \
 		test/enable-gprs \
 		test/disable-gprs \
-		test/get-icon
+		test/get-icon \
+		test/set-fast-dormancy \
+		test/test-push-notification \
+		test/test-smart-messaging \
+		test/send-vcard
 
 @TEST_TRUE at testdir = $(pkglibdir)/test
 @TEST_TRUE at test_SCRIPTS = $(test_scripts)
-conf_files = src/ofono.conf plugins/modem.conf
+conf_files = src/ofono.conf plugins/phonesim.conf
 EXTRA_DIST = src/genbuiltin $(conf_files) $(udev_files) \
 					$(doc_files) $(test_scripts)
 
@@ -1081,8 +1101,6 @@
 	@: > plugins/$(DEPDIR)/$(am__dirstamp)
 plugins/udev.$(OBJEXT): plugins/$(am__dirstamp) \
 	plugins/$(DEPDIR)/$(am__dirstamp)
-plugins/modemconf.$(OBJEXT): plugins/$(am__dirstamp) \
-	plugins/$(DEPDIR)/$(am__dirstamp)
 gisi/$(am__dirstamp):
 	@$(MKDIR_P) gisi
 	@: > gisi/$(am__dirstamp)
@@ -1346,13 +1364,14 @@
 drivers/stemodem/radio-settings.$(OBJEXT):  \
 	drivers/stemodem/$(am__dirstamp) \
 	drivers/stemodem/$(DEPDIR)/$(am__dirstamp)
+drivers/stemodem/caif_rtnl.$(OBJEXT):  \
+	drivers/stemodem/$(am__dirstamp) \
+	drivers/stemodem/$(DEPDIR)/$(am__dirstamp)
 drivers/stemodem/gprs-context.$(OBJEXT):  \
 	drivers/stemodem/$(am__dirstamp) \
 	drivers/stemodem/$(DEPDIR)/$(am__dirstamp)
 plugins/phonesim.$(OBJEXT): plugins/$(am__dirstamp) \
 	plugins/$(DEPDIR)/$(am__dirstamp)
-plugins/atgen.$(OBJEXT): plugins/$(am__dirstamp) \
-	plugins/$(DEPDIR)/$(am__dirstamp)
 plugins/g1.$(OBJEXT): plugins/$(am__dirstamp) \
 	plugins/$(DEPDIR)/$(am__dirstamp)
 plugins/wavecom.$(OBJEXT): plugins/$(am__dirstamp) \
@@ -1395,6 +1414,8 @@
 	examples/$(DEPDIR)/$(am__dirstamp)
 plugins/smart-messaging.$(OBJEXT): plugins/$(am__dirstamp) \
 	plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/push-notification.$(OBJEXT): plugins/$(am__dirstamp) \
+	plugins/$(DEPDIR)/$(am__dirstamp)
 src/$(am__dirstamp):
 	@$(MKDIR_P) src
 	@: > src/$(am__dirstamp)
@@ -1458,6 +1479,8 @@
 src/simfs.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
 src/audio-settings.$(OBJEXT): src/$(am__dirstamp) \
 	src/$(DEPDIR)/$(am__dirstamp)
+src/smsagent.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
 src/ofonod$(EXEEXT): $(src_ofonod_OBJECTS) $(src_ofonod_DEPENDENCIES) src/$(am__dirstamp)
 	@rm -f src/ofonod$(EXEEXT)
 	$(AM_V_CCLD)$(src_ofonod_LINK) $(src_ofonod_OBJECTS) $(src_ofonod_LDADD) $(LIBS)
@@ -1620,6 +1643,7 @@
 	-rm -f drivers/mbmmodem/stk.$(OBJEXT)
 	-rm -f drivers/nwmodem/nwmodem.$(OBJEXT)
 	-rm -f drivers/nwmodem/radio-settings.$(OBJEXT)
+	-rm -f drivers/stemodem/caif_rtnl.$(OBJEXT)
 	-rm -f drivers/stemodem/gprs-context.$(OBJEXT)
 	-rm -f drivers/stemodem/radio-settings.$(OBJEXT)
 	-rm -f drivers/stemodem/stemodem.$(OBJEXT)
@@ -1661,7 +1685,6 @@
 	-rm -f gisi/server.$(OBJEXT)
 	-rm -f gisi/socket.$(OBJEXT)
 	-rm -f gisi/verify.$(OBJEXT)
-	-rm -f plugins/atgen.$(OBJEXT)
 	-rm -f plugins/bluetooth.$(OBJEXT)
 	-rm -f plugins/caif.$(OBJEXT)
 	-rm -f plugins/calypso.$(OBJEXT)
@@ -1672,13 +1695,13 @@
 	-rm -f plugins/ifx.$(OBJEXT)
 	-rm -f plugins/isigen.$(OBJEXT)
 	-rm -f plugins/mbm.$(OBJEXT)
-	-rm -f plugins/modemconf.$(OBJEXT)
 	-rm -f plugins/n900.$(OBJEXT)
 	-rm -f plugins/nokia-gpio.$(OBJEXT)
 	-rm -f plugins/nokia.$(OBJEXT)
 	-rm -f plugins/novatel.$(OBJEXT)
 	-rm -f plugins/palmpre.$(OBJEXT)
 	-rm -f plugins/phonesim.$(OBJEXT)
+	-rm -f plugins/push-notification.$(OBJEXT)
 	-rm -f plugins/smart-messaging.$(OBJEXT)
 	-rm -f plugins/ste.$(OBJEXT)
 	-rm -f plugins/udev.$(OBJEXT)
@@ -1710,6 +1733,7 @@
 	-rm -f src/simfs.$(OBJEXT)
 	-rm -f src/simutil.$(OBJEXT)
 	-rm -f src/sms.$(OBJEXT)
+	-rm -f src/smsagent.$(OBJEXT)
 	-rm -f src/smsutil.$(OBJEXT)
 	-rm -f src/ssn.$(OBJEXT)
 	-rm -f src/stk.$(OBJEXT)
@@ -1798,6 +1822,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/mbmmodem/$(DEPDIR)/stk.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/nwmodem/$(DEPDIR)/nwmodem.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/nwmodem/$(DEPDIR)/radio-settings.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at drivers/stemodem/$(DEPDIR)/caif_rtnl.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/stemodem/$(DEPDIR)/gprs-context.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/stemodem/$(DEPDIR)/radio-settings.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/stemodem/$(DEPDIR)/stemodem.Po at am__quote@
@@ -1839,7 +1864,6 @@
 @AMDEP_TRUE@@am__include@ @am__quote at gisi/$(DEPDIR)/server.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gisi/$(DEPDIR)/socket.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gisi/$(DEPDIR)/verify.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/atgen.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/bluetooth.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/caif.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/calypso.Po at am__quote@
@@ -1850,13 +1874,13 @@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/ifx.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/isigen.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/mbm.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/modemconf.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/n900.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/nokia-gpio.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/nokia.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/novatel.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/palmpre.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/phonesim.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/push-notification.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/smart-messaging.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/ste.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/udev.Po at am__quote@
@@ -1888,6 +1912,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/simfs.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/simutil.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/sms.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/smsagent.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/smsutil.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/ssn.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/stk.Po at am__quote@
--- TODO
+++ TODO
@@ -1,4 +1,4 @@
-Background
+pBackground
 ==========
 
 - Priority scale: High, Medium and Low
@@ -31,38 +31,6 @@
   Priority: Low
   Complexity: C8
 
-- Source / Destination port addressing scheme from 3GPP 23.040.  A special
-  header is used to indicate the source / destination port of the application
-  this SMS message belongs to.  Such messages which are encoded in 8-bit should
-  be decoded by oFono automatically.  The handler associated with this source /
-  destination port is called with the decoded stream.
-
-  Proposed solution involves a registration / callback framework in the SMS
-  atom for plugins to register handlers.  Default (wildcard) handler is also
-  desired.
-
-  Priority: High
-  Complexity: C2
-  Owner: Aki Niemi <aki.niemi at nokia.com>
-
-- Smart Messaging Service.  This is a separate standard from Nokia and uses
-  the 3GPP 23.040 source / destination addressing scheme.  The main aim of
-  this task is to support vCards and vCalendar objects over SMS.
-
-  Proposed solution is to add a plugin with an extension API to send / receive
-  such messages.
-
-  Priority: Medium
-  Complexity: C2
-  Depends: Source / Destination port scheme
-
-- WAP PUSH notification support for MMS.  oFono should be able to receive
-  WAP PUSH messages over SMS, parse them and report them to the upper level
-  MMS stack.
-
-  Priority: Low
-  Complexity: C8
-
 - See / Cancel pending SMS messages over DBus.  When oFono sends SMS messages
   the method call is only returned when the message has been submitted to the
   network.  Instead we should return an object path and allow cancellation of
@@ -70,6 +38,7 @@
 
   Priority: High
   Complexity: C2
+  Owner: Yang Gu <yang.gu at intel.com>
 
 - Persist outgoing SMS messages.  Currently oFono persists incoming messages
   that are fragmented.  However oFono does not persist queued outgoing
@@ -107,23 +76,30 @@
   Complexity: C4
   Owner: Rajesh Kadhiravan Nagaiah <Rajesh.Nagaiah at elektrobit.com>
 
-SIM / SIM File system
-=====================
-
-- Barred Dialing numbers support.  BDN will not be supported by oFono.
-  If BDN service enabled SIM is used, oFono will go into emergency mode.
+- Asynchronously acknowledge SMS DELIVER messages sent by the SMS driver
+  to core using ofono_sms_deliver_notify().  This may require the struct
+  ofono_sms_driver to be extended with one more function pointer like:
+    void (*deliver_ack)(unsigned char *pdu, int len, cb_t cb, void *data)
+  because currently messages are automatically acknowledged by either the
+  modem (this is the case of some AT modems) or the driver right after
+  ofono_sms_deliver_notify() and a failure to deliver at an upper level is
+  ignored.  The PDU can be an RP-ACK or RP-ERROR message with optional
+  TP-User-Content element, for example if returned from USIM Data Download.
 
   Priority: Low
   Complexity: C2
-  Owner: Jeevaka Badrappan <jeevaka.badrappan at elektrobit.com>
+
+SIM / SIM File system
+=====================
 
 - Read / Write EFcfis.  Call forwarding settings can be bootstrapped on the
   SIM for faster notification of the user that call forwarding is active.
   These settings are stored in EFcfis.  oFono should read these settings and
-  update the call history atom appropriately.
+  update the call forwarding atom appropriately.
 
   Priority: Low
   Complexity: C2
+  Owner: Jeevaka Badrappan <jeevaka.badrappan at elektrobit.com>
 
 - SIM Call History plugin.  New UICCs support four new SIM elementary files
   for storing call history information on the SIM: EFici, EFict, EFoci, EFoct.
@@ -157,6 +133,12 @@
   Complexity: C2
   Owner: Pekka Pessi <pekka.pessi at nokia.com>
 
+- Check SIM pin status if sim_change_pin fails. If it is anything other than
+  READY, we must tear the state back to pre_sim state and set the modem offline.
+
+  Priority: Medium
+  Complexity: C1
+  Owner: Marit Henriksen <marit.henriksen at stericsson.com>
 
 Modem Emulator
 ==============
@@ -227,13 +209,6 @@
   Priority: Low
   Complexity: C4
 
-- On-demand SOCKS5 proxy support for MMS contexts.  When an MMS context type
-  is created, oFono should create a SOCKS5 proxy.  When a client connects to
-  the proxy, oFono can activate the GPRS context on-demand.
-
-  Priority: High
-  Complexity: C2
-
 - Add support for IPv6 GPRS contexts.
 
   Priority: Medium
@@ -333,14 +308,6 @@
 Sim Toolkit
 ===========
 
-- Support the Play Tone proactive command.  This should be implemented
-  by making the appropriate call into the SimToolkitAgent.  The sound
-  that will be played will be determined by the agent based on the sound
-  type.
-
-  Priority: High
-  Complexity: C2
-
 - Support Refresh proactive command.  The specification defines 7 types
   of Refresh types:
 	- NAA Initialization
@@ -382,18 +349,6 @@
   Complexity: C2
   Owner: Andrzej Zaborowski <andrew.zaborowski at intel.com>
 
-- Support Send SS proactive command.
-
-  Priority: High
-  Complexity: C4
-  Owner: Yang Gu <yang.gu at intel.com>
-
-- Support Send USSD proactive command.
-
-  Priority: High
-  Complexity: C4
-  Owner: Yang Gu <yang.gu at intel.com>
-
 - Support Set Up Call proactive command.  If the UICC has indicated that
   the user should be informed, then the SimToolkitAgent will be called
   in order to request user's confirmation of the call setup.  If the user
@@ -403,21 +358,22 @@
   Priority: High
   Complexity: C8
 
-- Support 'SMS-PP' Download Envelope.  Whenever a special type of SMS is
-  received indicating that this is an SMS-PP Download message, this message
-  will be sent to the SIM via an SMS-PP Download Envelope.  No user interaction
-  will be required.  If the modem hardware supports this transparently, then
-  no indication to oFono will be necessary.
-
-  Priority: High
-  Complexity: C2
-
 - Support Language Notification proactive command.
 
   Priority: Medium
   Complexity: C1
   Owner: Jeevaka Badrappan <jeevaka.badrappan at elektrobit.com>
 
+- Support Provide Local Information.  Whenever oFono receives a Provide Local
+  Information proactive command, it should check the type of the information
+  requested.  If the information is about the time/date or the language of the
+  terminal, it should respond to the command with the appropriate terminal
+  response.  Otherwise, it will respond with an error.
+
+  Priority: High
+  Complexity: C2
+  Owner: Yang Gu <yang.gu at intel.com>
+
 
 Emergency Calls
 ===============
@@ -466,7 +422,7 @@
 
   Priority: High
   Complexity: C1
-
+  Owner: John Mathew <john.mathew at elektrobit.com>
 
 Miscellaneous
 =============
@@ -481,6 +437,7 @@
 
 - Frequency Band Selection.  Add frequency band selection capability to the
   RadioSettings atom.
+  Owner: Lucas De Marchi <lucas.demarchi at profusion.mobi>
 
   This feature is not discussed in 27.007, thus manufacturer specific commands
   are required.
@@ -507,6 +464,7 @@
 
   Priority: Medium
   Complexity: C2
+  Owner: Gustavo F Padovan <padovan at profusion.mobi>
 
 - Add support for Modem 'power down & lock' and 'unlock & power up'.  When the
   'power down & lock' is given, the calling application is noted and all
@@ -518,26 +476,7 @@
 
   Priority: Medium
   Complexity: C2
-
-- Add a property for Fast Dormancy in the RadioSettings atom. This property
-  will enable or disable Fast Dormancy. Fast Dormancy refers to UE initiated
-  release of radio resources quickly after a burst of data transfer has ended.
-  Normally, radio resources are released by the network after a timeout
-  configured by the network operator. Fast Dormancy allows the modem to release
-  radio resources more quickly. Typically, fast dormancy would be enabled
-  if no data transfer is predicted to occur in the near future (e.g. end user
-  is not actively using the device). This is a major power-saving feature for
-  mobile devices, but can be ignored for USB sticks or PCI devices.
-
-  If the modem does not support such a feature the property should never be
-  exposed to the user.
-
-  This feature is not discussed in 27.007, thus manufacturer specific commands
-  are required.
-
-  Priority: High
-  Complexity: C1
-  Owner: Mika Liljeberg <mika.liljeberg at nokia.com>
+  Owner: Gustavo F Padovan <padovan at profusion.mobi>
 
 - TTY (hearing impaired) support.  Add a new oFono atom type that will enable
   the user to enable or disable the TTY support on the modem.  Support for
@@ -548,6 +487,7 @@
 
   Priority: High
   Complexity: C1
+  Owner: Lucas De Marchi <lucas.demarchi at profusion.mobi>
 
 - Add support for GPS power control atom.  Many modem manufacturers provide
   a GPS unit with their modem hardware.  This unit can be turned on or off
@@ -558,3 +498,11 @@
 
   Priority: Low
   Complexity: C4
+
+- Add Location Service API for providing basic E911 suport.
+  This will be based on the 27.007 defined AT commands using
+  XML for transport of positioning request and responses.
+
+  Priority: Medium
+  Complexity: C2
+  Owner: Sjur Brændeland <sjur.brandeland at stericsson.com>
--- configure
+++ configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.63 for ofono 0.33.
+# Generated by GNU Autoconf 2.63 for ofono 0.36.
 #
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
 # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
@@ -743,8 +743,8 @@
 # Identity of this package.
 PACKAGE_NAME='ofono'
 PACKAGE_TARNAME='ofono'
-PACKAGE_VERSION='0.33'
-PACKAGE_STRING='ofono 0.33'
+PACKAGE_VERSION='0.36'
+PACKAGE_STRING='ofono 0.36'
 PACKAGE_BUGREPORT=''
 
 ac_default_prefix=/usr/local
@@ -790,10 +790,12 @@
 LIBOBJS
 DATAFILES_FALSE
 DATAFILES_TRUE
-ATMODEM_FALSE
-ATMODEM_TRUE
 ISIMODEM_FALSE
 ISIMODEM_TRUE
+PHONESIM_FALSE
+PHONESIM_TRUE
+ATMODEM_FALSE
+ATMODEM_TRUE
 UDEV_FALSE
 UDEV_TRUE
 UDEV_DATADIR
@@ -948,8 +950,9 @@
 enable_systemd
 enable_capng
 enable_udev
-enable_isimodem
 enable_atmodem
+enable_phonesim
+enable_isimodem
 enable_datafiles
 '
       ac_precious_vars='build_alias
@@ -1526,7 +1529,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures ofono 0.33 to adapt to many kinds of systems.
+\`configure' configures ofono 0.36 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1596,7 +1599,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of ofono 0.33:";;
+     short | recursive ) echo "Configuration of ofono 0.36:";;
    esac
   cat <<\_ACEOF
 
@@ -1623,8 +1626,9 @@
   --enable-systemd        enable systemd support
   --enable-capng          enable capabilities support
   --disable-udev          don't use udev support even if available
-  --disable-isimodem      disable PhoNet/ISI modem support
   --disable-atmodem       disable ETSI AT modem support
+  --disable-phonesim      disable Phone simulator support
+  --disable-isimodem      disable PhoNet/ISI modem support
   --disable-datafiles     don't install configuration and data files
 
 Optional Packages:
@@ -1729,7 +1733,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-ofono configure 0.33
+ofono configure 0.36
 generated by GNU Autoconf 2.63
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1743,7 +1747,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by ofono $as_me 0.33, which was
+It was created by ofono $as_me 0.36, which was
 generated by GNU Autoconf 2.63.  Invocation command line was
 
   $ $0 $@
@@ -2593,7 +2597,7 @@
 
 # Define the identity of the package.
  PACKAGE='ofono'
- VERSION='0.33'
+ VERSION='0.36'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -5811,13 +5815,13 @@
 else
   lt_cv_nm_interface="BSD nm"
   echo "int some_variable = 0;" > conftest.$ac_ext
-  (eval echo "\"\$as_me:5814: $ac_compile\"" >&5)
+  (eval echo "\"\$as_me:5818: $ac_compile\"" >&5)
   (eval "$ac_compile" 2>conftest.err)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:5817: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval echo "\"\$as_me:5821: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
   (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:5820: output\"" >&5)
+  (eval echo "\"\$as_me:5824: output\"" >&5)
   cat conftest.out >&5
   if $GREP 'External.*some_variable' conftest.out > /dev/null; then
     lt_cv_nm_interface="MS dumpbin"
@@ -7022,7 +7026,7 @@
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 7025 "configure"' > conftest.$ac_ext
+  echo '#line 7029 "configure"' > conftest.$ac_ext
   if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -8845,11 +8849,11 @@
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8848: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8852: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:8852: \$? = $ac_status" >&5
+   echo "$as_me:8856: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -9184,11 +9188,11 @@
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9187: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9191: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:9191: \$? = $ac_status" >&5
+   echo "$as_me:9195: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -9289,11 +9293,11 @@
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9292: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9296: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:9296: \$? = $ac_status" >&5
+   echo "$as_me:9300: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -9344,11 +9348,11 @@
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9347: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9351: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:9351: \$? = $ac_status" >&5
+   echo "$as_me:9355: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -12147,7 +12151,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12150 "configure"
+#line 12154 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12243,7 +12247,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12246 "configure"
+#line 12250 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12681,12 +12685,12 @@
     pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.16\"") >&5
-  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.16") 2>&5
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.22\"") >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.22") 2>&5
   ac_status=$?
   $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
   (exit $ac_status); }; then
-  pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.16" 2>/dev/null`
+  pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.22" 2>/dev/null`
 else
   pkg_failed=yes
 fi
@@ -12697,12 +12701,12 @@
     pkg_cv_GLIB_LIBS="$GLIB_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.16\"") >&5
-  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.16") 2>&5
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.22\"") >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.22") 2>&5
   ac_status=$?
   $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
   (exit $ac_status); }; then
-  pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.16" 2>/dev/null`
+  pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.22" 2>/dev/null`
 else
   pkg_failed=yes
 fi
@@ -12720,21 +12724,21 @@
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "glib-2.0 >= 2.16" 2>&1`
+	        GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "glib-2.0 >= 2.22" 2>&1`
         else
-	        GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors "glib-2.0 >= 2.16" 2>&1`
+	        GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors "glib-2.0 >= 2.22" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
 	echo "$GLIB_PKG_ERRORS" >&5
 
 	{ $as_echo "$as_me:$LINENO: result: no" >&5
 $as_echo "no" >&6; }
-                { { $as_echo "$as_me:$LINENO: error: GLib >= 2.16 is required" >&5
-$as_echo "$as_me: error: GLib >= 2.16 is required" >&2;}
+                { { $as_echo "$as_me:$LINENO: error: GLib >= 2.22 is required" >&5
+$as_echo "$as_me: error: GLib >= 2.22 is required" >&2;}
    { (exit 1); exit 1; }; }
 elif test $pkg_failed = untried; then
-	{ { $as_echo "$as_me:$LINENO: error: GLib >= 2.16 is required" >&5
-$as_echo "$as_me: error: GLib >= 2.16 is required" >&2;}
+	{ { $as_echo "$as_me:$LINENO: error: GLib >= 2.22 is required" >&5
+$as_echo "$as_me: error: GLib >= 2.22 is required" >&2;}
    { (exit 1); exit 1; }; }
 else
 	GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS
@@ -13355,20 +13359,6 @@
 fi
 
 
-# Check whether --enable-isimodem was given.
-if test "${enable_isimodem+set}" = set; then
-  enableval=$enable_isimodem; enable_isimodem=${enableval}
-fi
-
- if test "${enable_isimodem}" != "no"; then
-  ISIMODEM_TRUE=
-  ISIMODEM_FALSE='#'
-else
-  ISIMODEM_TRUE='#'
-  ISIMODEM_FALSE=
-fi
-
-
 # Check whether --enable-atmodem was given.
 if test "${enable_atmodem+set}" = set; then
   enableval=$enable_atmodem; enable_atmodem=${enableval}
@@ -13383,6 +13373,35 @@
 fi
 
 
+# Check whether --enable-phonesim was given.
+if test "${enable_phonesim+set}" = set; then
+  enableval=$enable_phonesim; enable_phonesim=${enableval}
+fi
+
+ if test "${enable_phonesim}" != "no" &&
+					test "${enable_atmodem}" != "no"; then
+  PHONESIM_TRUE=
+  PHONESIM_FALSE='#'
+else
+  PHONESIM_TRUE='#'
+  PHONESIM_FALSE=
+fi
+
+
+# Check whether --enable-isimodem was given.
+if test "${enable_isimodem+set}" = set; then
+  enableval=$enable_isimodem; enable_isimodem=${enableval}
+fi
+
+ if test "${enable_isimodem}" != "no"; then
+  ISIMODEM_TRUE=
+  ISIMODEM_FALSE='#'
+else
+  ISIMODEM_TRUE='#'
+  ISIMODEM_FALSE=
+fi
+
+
 # Check whether --enable-datafiles was given.
 if test "${enable_datafiles+set}" = set; then
   enableval=$enable_datafiles; enable_datafiles=${enableval}
@@ -13585,17 +13604,24 @@
 Usually this means the macro was only invoked conditionally." >&2;}
    { (exit 1); exit 1; }; }
 fi
-if test -z "${ISIMODEM_TRUE}" && test -z "${ISIMODEM_FALSE}"; then
-  { { $as_echo "$as_me:$LINENO: error: conditional \"ISIMODEM\" was never defined.
+if test -z "${ATMODEM_TRUE}" && test -z "${ATMODEM_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"ATMODEM\" was never defined.
 Usually this means the macro was only invoked conditionally." >&5
-$as_echo "$as_me: error: conditional \"ISIMODEM\" was never defined.
+$as_echo "$as_me: error: conditional \"ATMODEM\" was never defined.
 Usually this means the macro was only invoked conditionally." >&2;}
    { (exit 1); exit 1; }; }
 fi
-if test -z "${ATMODEM_TRUE}" && test -z "${ATMODEM_FALSE}"; then
-  { { $as_echo "$as_me:$LINENO: error: conditional \"ATMODEM\" was never defined.
+if test -z "${PHONESIM_TRUE}" && test -z "${PHONESIM_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"PHONESIM\" was never defined.
 Usually this means the macro was only invoked conditionally." >&5
-$as_echo "$as_me: error: conditional \"ATMODEM\" was never defined.
+$as_echo "$as_me: error: conditional \"PHONESIM\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${ISIMODEM_TRUE}" && test -z "${ISIMODEM_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"ISIMODEM\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"ISIMODEM\" was never defined.
 Usually this means the macro was only invoked conditionally." >&2;}
    { (exit 1); exit 1; }; }
 fi
@@ -13928,7 +13954,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by ofono $as_me 0.33, which was
+This file was extended by ofono $as_me 0.36, which was
 generated by GNU Autoconf 2.63.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -13991,7 +14017,7 @@
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_version="\\
-ofono config.status 0.33
+ofono config.status 0.36
 configured by $0, generated by GNU Autoconf 2.63,
   with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
--- configure.ac
+++ configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(ofono, 0.33)
+AC_INIT(ofono, 0.36)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects])
 AM_CONFIG_HEADER(config.h)
@@ -67,8 +67,8 @@
 AC_CHECK_LIB(dl, dlopen, dummy=yes,
 			AC_MSG_ERROR(dynamic linking loader is required))
 
-PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, dummy=yes,
-				AC_MSG_ERROR(GLib >= 2.16 is required))
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.22, dummy=yes,
+				AC_MSG_ERROR(GLib >= 2.22 is required))
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
@@ -153,16 +153,22 @@
 AC_SUBST(UDEV_LIBS)
 AM_CONDITIONAL(UDEV, test "${enable_udev}" = "yes")
 
-AC_ARG_ENABLE(isimodem, AC_HELP_STRING([--disable-isimodem],
-				[disable PhoNet/ISI modem support]),
-					[enable_isimodem=${enableval}])
-AM_CONDITIONAL(ISIMODEM, test "${enable_isimodem}" != "no")
-
 AC_ARG_ENABLE(atmodem, AC_HELP_STRING([--disable-atmodem],
 				[disable ETSI AT modem support]),
 					[enable_atmodem=${enableval}])
 AM_CONDITIONAL(ATMODEM, test "${enable_atmodem}" != "no")
 
+AC_ARG_ENABLE(phonesim, AC_HELP_STRING([--disable-phonesim],
+				[disable Phone simulator support]),
+					[enable_phonesim=${enableval}])
+AM_CONDITIONAL(PHONESIM, test "${enable_phonesim}" != "no" &&
+					test "${enable_atmodem}" != "no")
+
+AC_ARG_ENABLE(isimodem, AC_HELP_STRING([--disable-isimodem],
+				[disable PhoNet/ISI modem support]),
+					[enable_isimodem=${enableval}])
+AM_CONDITIONAL(ISIMODEM, test "${enable_isimodem}" != "no")
+
 AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
 			[don't install configuration and data files]),
 					[enable_datafiles=${enableval}])
--- doc/call-volume-api.txt
+++ doc/call-volume-api.txt
@@ -15,7 +15,7 @@
 		void SetProperty(string property, variant value)
 
 			Changes the value of the specified property. Only
-			properties that are listed as read-write are
+			properties that are listed as readwrite are
 			changeable. On success a PropertyChanged signal
 			will be emitted.
 
--- doc/cell-broadcast-api.txt
+++ doc/cell-broadcast-api.txt
@@ -15,7 +15,7 @@
 		void SetProperty(string property, variant value)
 
 			Changes the value of the specified property. Only
-			properties that are listed as read-write are
+			properties that are listed as readwrite are
 			changeable. On success a PropertyChanged signal
 			will be emitted.
 
--- doc/connman-api.txt
+++ doc/connman-api.txt
@@ -155,6 +155,7 @@
 				"internet" - General internet connectivity
 				"mms" - Used by MMS related services
 				"wap" - Used by WAP related services
+				"ims" - Used by IMS related services
 
 		string Username [readwrite]
 
@@ -211,3 +212,30 @@
 
 				Holds the gateway IP for this connection.
 
+			string Proxy [readonly, MMS only]
+
+				Holds the current proxy information for
+				using this context.
+
+				In combination with the Interface value
+				this allows access to the services offered
+				by this context.
+
+				It is possible that this reflects just the
+				MessageProxy property if such a routing can
+				be set up.  However this can also be pointing
+				to a local proxy on 127.0.0.1 and then using
+				the loopback interace lo for it.
+
+				Users of this context should bind to the
+				provided interface and only attempt access
+				via this proxy.  All other values are left
+				out in this case.
+
+		string MessageProxy [readwrite, MMS only]
+
+			Holds the MMS Proxy setting.
+
+		string MessageCenter [readwrite, MMS only]
+
+			Holds the MMSC setting.
--- doc/features.txt
+++ doc/features.txt
@@ -77,6 +77,24 @@
   modem.  The appropriate terminal response is sent to the SIM once the DTMF
   tones have been played or the call has been disconnected.
 
+  NOTE: This command can also be handled by the modem.
+
+- Play Tone proactive command.  Whenever oFono receives a Play Tone proactive
+  command it checks whether the tone is to be continuous/looped or played once.
+  It then calls the SimToolkitAgent PlayTone or LoopTone method as appropriate.
+  The sound that will be played will be determined based on the sound type
+  that is passed to the agent.  It is up to the system integrator to provide
+  the appropriate sounds.
+
+- Send USSD proactive command.  Whenever oFono receives a Send USSD proactive
+  command it checks whether there are any USSD / SS operations in progress.
+  If an operation is in progress, the appropriate terminal response is sent
+  without performing the Send USSD operation.  Otherwise the USSD string
+  is sent to the network, and the response is sent back to the SIM in the
+  terminal response.
+
+  NOTE: This command can also be handled by the modem.
+
 - Sim icon support.  oFono supports icons that are stored on the SIM.  If the
   SIM notifies oFono that an icon is available for a particular proactive
   command, oFono passes this information to the UI.  The UI is able to obtain
@@ -104,6 +122,29 @@
   CBS-PP Download envelope.  No user interaction is required or signaled
   whenever this occurs.
 
+- SMS-PP Download envelope support.  When oFono receives an sms message
+  addressed to the SIM, it is dispatched to the SIM using the SMS-PP Download
+  envelope.  No user interaction is required or signaled whenever this occurs.
+
+  Please note that many current modems do not support returning RP-ACK and
+  RP-ERROR acknowledgement PDUs back to the network.  This is required by the
+  CAT specification for SMS-PP Download.  E.g. the sim response to the SMS-PP
+  Download Envelope is to be stored in an RP-ACK / RP-ERROR PDU and returned to
+  the network.  It is thus anticipated that modems will transparently handle
+  this feature in the firmware.
+
+The following commands are expected to be handled by the modem:
+
+- Send SS proactive command.  oFono does not explicitly support this proactive
+  command since AT modems do not provide the low level information necessary
+  for oFono to generate a valid response.  The modem (or driver) shall handle
+  this command.  Optionally the modem (or driver) can inform oFono that the
+  proactive command has been received and is being handled by the modem, as
+  well as when the terminal response has been sent by the modem.  oFono will
+  display the necessary user information for this time period if this
+  information is included in the proactive command.
+
+
 Short Messaging Service
 =======================
 
@@ -121,6 +162,33 @@
   status reports are received, the UI is notified either via DBus or history
   plugin API.
 
+- Source / Destination port addressing scheme from 3GPP 23.040.  A special
+  header is used to indicate the source / destination port of the application
+  this SMS message belongs to.  oFono provides a handler registration
+  framework where plugins can handle the reception of such messages.  The
+  handler can be registered to receive messages which contain a specific
+  source and destination port, or a wildcard.  When such messages are received,
+  they are matched against the registered handlers and dispatched appropriately.
+
+  oFono takes care of de-fragmentation of such SMS messages, so the handler
+  is informed only once the entire message has been received, and the data
+  payload has been extracted.
+
+- Smart Messaging Service - vCard support.  oFono provides the ability to send
+  and receive vCard objects through the SmartMessaging interface.  vCards can
+  be sent using the SendBusinessCard method and received using the
+  SmartMessagingAgent framework.
+
+- Smart Messaging Service - vCalendar support.  oFono provides the ability to
+  send and receive vCalendar objects through the SmartMessaging interface.
+  vCalendars can be sent using the SendAppointment method and received using
+  the SmartMessagingAgent framework.
+
+- WAP PUSH notification support.  oFono allows the reception of WAP PUSH
+  messages via SMS through the use of the PushNotification interface and the
+  PushNotificationAgent framework.
+
+
 GPRS
 ====
 
@@ -135,3 +203,16 @@
   check if FDN support is allocated and enabled in the SIM.  If enabled,
   oFono halts the SIM initialization procedure and the modem remains in the
   PRESIM state.  In this state oFono will only allow emergency calls.
+
+- Barred Dialing support.  oFono reads the necessary bits from the SIM to
+  check if BDN support is allocated and enabled in the SIM.  If enabled,
+  oFono halts the SIM initialization procedure and the modem remains in the
+  PRESIM state.  In this state oFono will only allow emergency calls.
+
+Radio settings
+==============
+
+- Fast dormancy support. A fast dormancy feature can be enabled in the
+  cellular modem to conserve power when the end user is not actively
+  using the device but some networking applications are online using
+  packet data.
--- doc/message-api.txt
+++ doc/message-api.txt
@@ -25,7 +25,7 @@
 		void SetProperty(string name, variant value)
 
 			Changes the value of the specified property. Only
-			properties that are listed as read-write are
+			properties that are listed as readwrite are
 			changeable. On success a PropertyChanged signal
 			will be emitted.
 
--- doc/message-waiting-api.txt
+++ doc/message-waiting-api.txt
@@ -15,7 +15,7 @@
 		void SetProperty(string property, variant value)
 
 			Changes the value of the specified property. Only
-			properties that are listed as read-write are
+			properties that are listed as readwrite are
 			changeable. On success a PropertyChanged signal
 			will be emitted.
 
--- doc/modem-api.txt
+++ doc/modem-api.txt
@@ -15,7 +15,7 @@
 		void SetProperty(string property, variant value)
 
 			Changes the value of the specified property. Only
-			properties that are listed as read-write are
+			properties that are listed as readwrite are
 			changeable. On success a PropertyChanged signal
 			will be emitted.
 
--- doc/network-api.txt
+++ doc/network-api.txt
@@ -15,7 +15,7 @@
 		void SetProperty(string name, variant value)
 
 			Changes the value of the specified property. Only
-			properties that are listed as read-write are
+			properties that are listed as readwrite are
 			changeable. On success a PropertyChanged signal
 			will be emitted.
 
--- doc/radio-settings-api.txt
+++ doc/radio-settings-api.txt
@@ -15,7 +15,7 @@
 		void SetProperty(string name, variant value)
 
 			Changes the value of the specified property. Only
-			properties that are listed as read-write are
+			properties that are listed as readwrite are
 			changeable. On success a PropertyChanged signal
 			will be emitted.
 
@@ -28,7 +28,7 @@
 			This signal indicates a changed value of the given
 			property.
 
-Properties	string TechnologyPreference [read-write]
+Properties	string TechnologyPreference [readwrite]
 
 			The current radio access selection mode, also known
 			as network preference.
@@ -42,24 +42,47 @@
 				"umts"	Only UMTS used for radio access.
 				"lte"	Only LTE used for radio acccess.
 
-		boolean	FastDormancy [read-write, optional]
+		boolean	FastDormancy [readwrite, optional]
 
-			This property will enable or disable fast
-			dormancy. Fast dormancy refers to UE initiated
-			release of radio resources quickly after a
-			burst of data transfer has ended. Normally,
+			This property will enable or disable the fast
+			dormancy feature in the modem. Fast dormancy
+			refers to a modem feature that allows the
+			modem to quickly release radio resources after
+			a burst of data transfer has ended. Normally,
 			radio resources are released by the network
-			after a timeout configured by the network
-			operator. Fast dormancy allows the modem to
-			release radio resources more quickly.
-			Typically, fast dormancy would be enabled if
-			no data transfer is predicted to occur in the
-			near future, for instance, when the end user
-			is not actively using the device. This is a
-			major power-saving feature for mobile devices,
-			but can be ignored for USB sticks or PCI
-			devices.
-
-			If the modem does not support such a feature
-			the property should never be exposed to the
-			user.
+			after a timeout configured by the network.
+			Fast dormancy allows the modem to release the
+			radio resources more quickly.
+
+			Fast dormancy is a major power-saving feature
+			for mobile devices. Typically, fast dormancy
+			would be enabled when the device is not being
+			interactively used by a human user and only
+			networking applications with keep-alive
+			traffic are active (e.g. mail client or a
+			presence application). In this case it is
+			desirable to release radio resources quickly
+			after a keep-alive transaction has ended,
+			since typically no network traffic will occur
+			until the next keep-alive transaction. Fast
+			dormancy should not be enabled during
+			interactive use because the release and setup
+			of radio resources introduces perceivable
+			delay for the end user.
+
+			The fast dormancy implementation in the modem
+			is vendor specific. The implementation should
+			try to release radio resources more quickly,
+			when the situation allows it, but should also
+			take care not to increase the signalling load
+			on the cellular network by releasing and
+			re-establishing radio resources too often. The
+			modem should adjust its behaviour to the 3GPP
+			release supported by the network and the
+			parameters set by the operator.
+
+			Fast dormancy can be ignored for externally
+			powered modems such as USB sticks or PCI
+			devices. If the modem does not support such a
+			feature the property should never be exposed
+			to the user.
--- doc/sim-api.txt
+++ doc/sim-api.txt
@@ -138,3 +138,10 @@
 
 			If FDN is enabled, oFono halts the SIM initialization
 			procedure and only emergency calls are allowed.
+
+		boolean BarredDialing [readonly]
+
+			True if Barred Dialing service is enabled in SIM card.
+
+			If BDN is enabled, oFono halts the SIM initialization
+			procedure and only emergency calls are allowed.
--- doc/voicecallmanager-api.txt
+++ doc/voicecallmanager-api.txt
@@ -18,10 +18,10 @@
 		object Dial(string number, string hide_callerid)
 
 			Initiates a new outgoing call. Returns the object path
-			to the newly created call. The clir variable holds
-			the CLIR override for this call.
+			to the newly created call. The hide_callerid variable
+			holds the CLIR override for this call.
 			The defines values are:
-				"" or "default" - Default (Netowrk) CLIR mode
+				"" or "default" - Default (Network) CLIR mode
 							is used
 				"enabled" - Hides callerid, CLIR Invocation
 						is used
--- drivers/atmodem/call-settings.c
+++ drivers/atmodem/call-settings.c
@@ -44,6 +44,7 @@
 static const char *colp_prefix[] = { "+COLP:", NULL };
 static const char *clip_prefix[] = { "+CLIP:", NULL };
 static const char *ccwa_prefix[] = { "+CCWA:", NULL };
+static const char *colr_prefix[] = { "+COLR:", NULL };
 
 static void ccwa_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
@@ -319,6 +320,57 @@
 	CALLBACK_WITH_FAILURE(cb, data);
 }
 
+static void colr_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_call_settings_status_cb_t cb = cbd->cb;
+	struct ofono_error error;
+	GAtResultIter iter;
+	int status;
+
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	if (!ok) {
+		cb(&error, -1, cbd->data);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	if (g_at_result_iter_next(&iter, "+COLR:") == FALSE)
+		goto error;
+
+	if (g_at_result_iter_next_number(&iter, &status) == FALSE)
+		goto error;
+
+	DBG("colr_query_cb: network: %d", status);
+
+	cb(&error, status, cbd->data);
+	return;
+
+error:
+	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void at_colr_query(struct ofono_call_settings *cs,
+				ofono_call_settings_status_cb_t cb, void *data)
+{
+	GAtChat *chat = ofono_call_settings_get_data(cs);
+	struct cb_data *cbd = cb_data_new(cb, data);
+
+	if (!cbd)
+		goto error;
+
+	if (g_at_chat_send(chat, "AT+COLR", colr_prefix,
+				colr_query_cb, cbd, g_free) > 0)
+		return;
+
+error:
+	g_free(cbd);
+
+	CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
 static gboolean at_call_settings_register(gpointer user)
 {
 	struct ofono_call_settings *cs = user;
@@ -355,7 +407,7 @@
 	.colp_query = at_colp_query,
 	.clir_query = at_clir_query,
 	.clir_set = at_clir_set,
-	.colr_query = NULL,
+	.colr_query = at_colr_query,
 	.cw_query = at_ccwa_query,
 	.cw_set = at_ccwa_set,
 };
--- drivers/atmodem/gprs-context.c
+++ drivers/atmodem/gprs-context.c
@@ -83,10 +83,15 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	const char *dns[3];
 
+	DBG("");
+
 	dns[0] = dns1;
 	dns[1] = dns2;
 	dns[2] = 0;
 
+	ofono_info("IP: %s", local);
+	ofono_info("DNS: %s, %s", dns1, dns2);
+
 	gcd->state = STATE_ACTIVE;
 	CALLBACK_WITH_SUCCESS(gcd->up_cb, interface, TRUE, local,
 					STATIC_IP_NETMASK, NULL,
@@ -131,6 +136,8 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	GAtIO *io;
 
+	DBG("");
+
 	io = g_at_chat_get_io(gcd->chat);
 
 	g_at_chat_suspend(gcd->chat);
@@ -163,6 +170,8 @@
 	struct ofono_gprs_context *gc = user_data;
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
+	DBG("ok %d", ok);
+
 	if (!ok) {
 		struct ofono_error error;
 
@@ -186,6 +195,8 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	char buf[64];
 
+	DBG("ok %d", ok);
+
 	if (!ok) {
 		struct ofono_error error;
 
@@ -218,6 +229,8 @@
 	char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
 	int len;
 
+	DBG("cid %u", ctx->cid);
+
 	gcd->active_context = ctx->cid;
 	gcd->up_cb = cb;
 	gcd->cb_data = data;
@@ -240,12 +253,12 @@
 }
 
 static void at_gprs_deactivate_primary(struct ofono_gprs_context *gc,
-					unsigned int id,
+					unsigned int cid,
 					ofono_gprs_context_cb_t cb, void *data)
 {
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
-	DBG("");
+	DBG("cid %u", cid);
 
 	gcd->state = STATE_DISABLING;
 	gcd->down_cb = cb;
@@ -261,6 +274,8 @@
 	struct gprs_context_data *gcd;
 	struct stat st;
 
+	DBG("");
+
 	if (stat(TUN_SYSFS_DIR, &st) < 0) {
 		ofono_error("Missing support for TUN/TAP devices");
 		return -ENODEV;
--- drivers/atmodem/gprs.c
+++ drivers/atmodem/gprs.c
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <errno.h>
 
 #include <glib.h>
 
@@ -174,6 +175,31 @@
 	}
 }
 
+static void xdatastat_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs *gprs = user_data;
+	GAtResultIter iter;
+	int stat;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+XDATASTAT:"))
+		return;
+
+	if (!g_at_result_iter_next_number(&iter, &stat))
+
+	DBG("stat %d", stat);
+
+	switch (stat) {
+	case 0:
+		ofono_gprs_suspend_notify(gprs, GPRS_SUSPENDED_UNKNOWN_CAUSE);
+		break;
+	case 1:
+		ofono_gprs_resume_notify(gprs);
+		break;
+	}
+}
+
 static void gprs_initialized(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct ofono_gprs *gprs = user_data;
@@ -181,7 +207,17 @@
 
 	g_at_chat_register(gd->chat, "+CGEV:", cgev_notify, FALSE, gprs, NULL);
 	g_at_chat_register(gd->chat, "+CGREG:", cgreg_notify,
-				FALSE, gprs, NULL);
+					FALSE, gprs, NULL);
+
+	switch (gd->vendor) {
+	case OFONO_VENDOR_IFX:
+		/* Register for GPRS suspend notifications */
+		g_at_chat_register(gd->chat, "+XDATASTAT:", xdatastat_notify,
+						FALSE, gprs, NULL);
+		g_at_chat_send(gd->chat, "AT+XDATASTAT=1", none_prefix,
+						NULL, NULL, NULL);
+		break;
+	}
 
 	ofono_gprs_register(gprs);
 }
@@ -313,7 +349,10 @@
 	GAtChat *chat = data;
 	struct gprs_data *gd;
 
-	gd = g_new0(struct gprs_data, 1);
+	gd = g_try_new0(struct gprs_data, 1);
+	if (!gd)
+		return -ENOMEM;
+
 	gd->chat = g_at_chat_clone(chat);
 	gd->vendor = vendor;
 
--- drivers/atmodem/network-registration.c
+++ drivers/atmodem/network-registration.c
@@ -631,6 +631,23 @@
 				at_util_convert_signal_strength(strength));
 }
 
+static void ifx_xhomezr_notify(GAtResult *result, gpointer user_data)
+{
+	//struct ofono_netreg *netreg = user_data;
+	const char *label;
+	GAtResultIter iter;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+XHOMEZR:"))
+		return;
+
+	if (!g_at_result_iter_next_string(&iter, &label))
+		return;
+
+	ofono_info("Home zone: %s", label);
+}
+
 static void ifx_xciev_notify(GAtResult *result, gpointer user_data)
 {
 	struct ofono_netreg *netreg = user_data;
@@ -682,6 +699,24 @@
 
 static void ctzv_notify(GAtResult *result, gpointer user_data)
 {
+	//struct ofono_netreg *netreg = user_data;
+	//struct netreg_data *nd = ofono_netreg_get_data(netreg);
+	const char *tz;
+	GAtResultIter iter;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CTZV:"))
+		return;
+
+	if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
+		return;
+
+	DBG("tz %s", tz);
+}
+
+static void ifx_ctzv_notify(GAtResult *result, gpointer user_data)
+{
 	struct ofono_netreg *netreg = user_data;
 	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 	int year, mon, mday, hour, min, sec;
@@ -713,7 +748,7 @@
 	nd->time.year = 2000 + year;
 }
 
-static void ctzdst_notify(GAtResult *result, gpointer user_data)
+static void ifx_ctzdst_notify(GAtResult *result, gpointer user_data)
 {
 	struct ofono_netreg *netreg = user_data;
 	struct netreg_data *nd = ofono_netreg_get_data(netreg);
@@ -852,6 +887,55 @@
 	CALLBACK_WITH_FAILURE(cb, -1, data);
 }
 
+static void mbm_etzv_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_netreg *netreg = user_data;
+	struct netreg_data *nd = ofono_netreg_get_data(netreg);
+	int year, mon, mday, hour, min, sec;
+	const char *tz, *time, *timestamp;
+	GAtResultIter iter;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (g_at_result_iter_next(&iter, "*ETZV:") == FALSE)
+		return;
+
+	if (g_at_result_iter_next_string(&iter, &tz) == FALSE)
+		return;
+
+	if (g_at_result_iter_next_string(&iter, &time) == FALSE)
+		time = NULL;
+
+	if (g_at_result_iter_next_string(&iter, &timestamp) == FALSE)
+		timestamp = NULL;
+
+	DBG("tz %s time %s timestamp %s", tz, time, timestamp);
+
+	if (time == NULL) {
+		year = -1;
+		mon = -1;
+		mday = -1;
+		hour = -1;
+		min = -1;
+		sec = -1;
+	} else {
+		if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
+						&hour, &min, &sec) != 6)
+		return;
+	}
+
+	nd->time.utcoff = atoi(tz) * 15 * 60;
+
+	nd->time.sec = sec;
+	nd->time.min = min;
+	nd->time.hour = hour;
+	nd->time.mday = mday;
+	nd->time.mon = mon;
+	nd->time.year = year;
+
+	ofono_netreg_time_notify(netreg, &nd->time);
+}
+
 static void mbm_erinfo_notify(GAtResult *result, gpointer user_data)
 {
 	struct ofono_netreg *netreg = user_data;
@@ -1049,7 +1133,6 @@
 	ofono_netreg_remove(netreg);
 }
 
-
 static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct ofono_netreg *netreg = user_data;
@@ -1084,21 +1167,36 @@
 				NULL, NULL, NULL);
 		g_at_chat_send(nd->chat, "AT_OSQI?", none_prefix,
 				NULL, NULL, NULL);
+
+		/* Register for network time update reports */
+		g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify,
+						FALSE, netreg, NULL);
+		g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
+						NULL, NULL, NULL);
 		break;
 	case OFONO_VENDOR_MBM:
+		/* Enable network registration updates */
 		g_at_chat_send(nd->chat, "AT*E2REG=1", none_prefix,
-					NULL, NULL, NULL);
+						NULL, NULL, NULL);
 		g_at_chat_send(nd->chat, "AT*EREG=2", none_prefix,
-					NULL, NULL, NULL);
+						NULL, NULL, NULL);
 		g_at_chat_send(nd->chat, "AT*EPSB=1", none_prefix,
-					NULL, NULL, NULL);
+						NULL, NULL, NULL);
 
+		/* Register for network technology updates */
 		g_at_chat_send(nd->chat, "AT*ERINFO=1", none_prefix,
-					NULL, NULL, NULL);
+						NULL, NULL, NULL);
 		g_at_chat_register(nd->chat, "*ERINFO:", mbm_erinfo_notify,
-					FALSE, netreg, NULL);
+						FALSE, netreg, NULL);
+
+		/* Register for network time update reports */
+		g_at_chat_register(nd->chat, "*ETZV:", mbm_etzv_notify,
+						FALSE, netreg, NULL);
+		g_at_chat_send(nd->chat, "AT*ETZR=2", none_prefix,
+						NULL, NULL, NULL);
+
 		g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix,
-				cind_support_cb, netreg, NULL);
+					cind_support_cb, netreg, NULL);
 		return;
 	case OFONO_VENDOR_NOVATEL:
 		/*
@@ -1116,15 +1214,21 @@
 	case OFONO_VENDOR_IFX:
 		/* Register for specific signal strength reports */
 		g_at_chat_register(nd->chat, "+XCIEV:", ifx_xciev_notify,
-					FALSE, netreg, NULL);
+						FALSE, netreg, NULL);
 		g_at_chat_send(nd->chat, "AT+XMER=1", none_prefix,
 						NULL, NULL, NULL);
 
+		/* Register for home zone reports */
+		g_at_chat_register(nd->chat, "+XHOMEZR:", ifx_xhomezr_notify,
+						FALSE, netreg, NULL);
+		g_at_chat_send(nd->chat, "AT+XHOMEZR=1", none_prefix,
+						NULL, NULL, NULL);
+
 		/* Register for network time update reports */
-		g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify,
-					FALSE, netreg, NULL);
-		g_at_chat_register(nd->chat, "+CTZDST:", ctzdst_notify,
-					FALSE, netreg, NULL);
+		g_at_chat_register(nd->chat, "+CTZV:", ifx_ctzv_notify,
+						FALSE, netreg, NULL);
+		g_at_chat_register(nd->chat, "+CTZDST:", ifx_ctzdst_notify,
+						FALSE, netreg, NULL);
 		g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
 						NULL, NULL, NULL);
 		break;
--- drivers/atmodem/sim.c
+++ drivers/atmodem/sim.c
@@ -647,38 +647,6 @@
 	CALLBACK_WITH_FAILURE(cb, data);
 }
 
-static void at_pin_send_puk_cb(gboolean ok, GAtResult *result,
-				gpointer user_data)
-{
-	struct cb_data *cbd = user_data;
-	struct sim_data *sd = cbd->user;
-	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
-	struct ofono_error error;
-
-	decode_at_error(&error, g_at_result_final_response(result));
-
-	if (!ok)
-		goto done;
-
-	switch (sd->vendor) {
-	case OFONO_VENDOR_IFX:
-		/*
-		 * On the IFX modem, AT+CPIN? can return READY too
-		 * early and so use +XSIM notification to detect
-		 * the ready state of the SIM.
-		 */
-		sd->ready_id = g_at_chat_register(sd->chat, "+XSIM",
-							at_xsim_notify,
-							FALSE, cbd, g_free);
-		return;
-	}
-
-done:
-	cb(&error, cbd->data);
-
-	g_free(cbd);
-}
-
 static void at_pin_send_puk(struct ofono_sim *sim, const char *puk,
 				const char *passwd,
 				ofono_sim_lock_unlock_cb_t cb, void *data)
@@ -696,7 +664,7 @@
 	snprintf(buf, sizeof(buf), "AT+CPIN=\"%s\",\"%s\"", puk, passwd);
 
 	ret = g_at_chat_send(sd->chat, buf, none_prefix,
-				at_pin_send_puk_cb, cbd, NULL);
+				at_pin_send_cb, cbd, NULL);
 
 	memset(buf, 0, sizeof(buf));
 
--- drivers/calypsomodem/stk.c
+++ drivers/calypsomodem/stk.c
@@ -209,13 +209,26 @@
 
 static void satn_notify(GAtResult *result, gpointer user_data)
 {
+	struct ofono_stk *stk = user_data;
+	GAtResultIter iter;
+	const guint8 *pdu;
+	gint len;
+
 	DBG("");
 
-	/*
-	 * Proactive command has been handled by the modem.  Should
-	 * the core be notified?  For now we just ignore it because
-	 * we must not respond to the command.
-	 */
+	/* Proactive command has been handled by the modem. */
+	g_at_result_iter_init(&iter, result);
+
+	if (g_at_result_iter_next(&iter, "%SATN:") == FALSE)
+		return;
+
+	if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE)
+		return;
+
+	if (len == 0)
+		return;
+
+	ofono_stk_proactive_command_handled_notify(stk, len, pdu);
 }
 
 static void calypso_stk_register(gboolean ok, GAtResult *result,
--- drivers/hsomodem/radio-settings.c
+++ drivers/hsomodem/radio-settings.c
@@ -128,7 +128,7 @@
 	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
 	struct cb_data *cbd = cb_data_new(cb, data);
 	char buf[20];
-	int value;
+	int value = 5;
 
 	switch (mode) {
 	case OFONO_RADIO_ACCESS_MODE_ANY:
@@ -140,19 +140,19 @@
 	case OFONO_RADIO_ACCESS_MODE_UMTS:
 		value = 1;
 		break;
-	default:
-		CALLBACK_WITH_FAILURE(cb, data);
-		g_free(cbd);
-		return;
+	case OFONO_RADIO_ACCESS_MODE_LTE:
+		goto error;
 	}
 
 	snprintf(buf, sizeof(buf), "AT_OPSYS=%u,2", value);
 
 	if (g_at_chat_send(rsd->chat, buf, none_prefix,
-					opsys_modify_cb, cbd, g_free) == 0) {
-		CALLBACK_WITH_FAILURE(cb, data);
-		g_free(cbd);
-	}
+					opsys_modify_cb, cbd, g_free) > 0)
+		return;
+
+error:
+	CALLBACK_WITH_FAILURE(cb, data);
+	g_free(cbd);
 }
 
 static void opsys_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
--- drivers/huaweimodem/gprs-context.c
+++ drivers/huaweimodem/gprs-context.c
@@ -28,6 +28,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
+#include <sys/stat.h>
 
 #include <glib.h>
 
@@ -37,11 +38,11 @@
 
 #include "gatchat.h"
 #include "gatresult.h"
+#include "gattty.h"
 
 #include "huaweimodem.h"
 
-#define AUTH_BUF_LENGTH OFONO_GPRS_MAX_USERNAME_LENGTH + \
-			OFONO_GPRS_MAX_PASSWORD_LENGTH + 128
+#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun"
 
 static const char *none_prefix[] = { NULL };
 static const char *dhcp_prefix[] = { "^DHCP:", NULL };
@@ -51,6 +52,7 @@
 	unsigned int active_context;
 	unsigned int dhcp_source;
 	unsigned int dhcp_count;
+	guint ndis_watch;
 	union {
 		ofono_gprs_context_cb_t down_cb;	/* Down callback */
 		ofono_gprs_context_up_cb_t up_cb;	/* Up callback */
@@ -65,7 +67,7 @@
 	struct ofono_gprs_context *gc = user_data;
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
-	if (gcd->dhcp_count > 10)
+	if (gcd->dhcp_count > 20)
 		CALLBACK_WITH_FAILURE(gcd->up_cb, NULL, 0, NULL, NULL,
 						NULL, NULL, gcd->cb_data);
 	else
@@ -77,6 +79,40 @@
 	return FALSE;
 }
 
+static gboolean ndis_receive_callback(GIOChannel *channel,
+					GIOCondition cond, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	GIOStatus status;
+	gsize bytes_read;
+	char buf[1059];
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		gcd->ndis_watch = 0;
+		return FALSE;
+	}
+
+	status = g_io_channel_read_chars(channel, buf, sizeof(buf),
+							&bytes_read, NULL);
+
+	ofono_info("Received %zd bytes", bytes_read);
+
+	{
+		unsigned int i;
+		for (i = 0; i < bytes_read; i++)
+			printf("%02x ", buf[i]);
+		printf("\n");
+	}
+
+	if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) {
+		gcd->ndis_watch = 0;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static gboolean get_next_addr(GAtResultIter *iter, char **addr)
 {
 	const char *str;
@@ -88,7 +124,7 @@
 	val = strtol(str, NULL, 16);
 
 	if (addr)
-	        *addr = g_strdup_printf("%u.%u.%u.%u",
+		*addr = g_strdup_printf("%u.%u.%u.%u",
 					(val & 0x000000ff),
 					(val & 0x0000ff00) >> 8,
 					(val & 0x00ff0000) >> 16,
@@ -111,8 +147,9 @@
 	const char *dns[3];
 	struct ofono_modem *modem;
 	const char *devnode;
+	GIOChannel *channel;
 
-	DBG("");
+	DBG("ok %d", ok);
 
 	if (!ok) {
 		gcd->dhcp_source = g_timeout_add_seconds(1, dhcp_poll, gc);
@@ -137,7 +174,7 @@
 
 	ofono_info("Got the following parameters for context: %d",
 							gcd->active_context);
-	ofono_info("IP: %s, Gateway: %s", ip, gateway);
+	ofono_info("IP: %s  Gateway: %s", ip, gateway);
 	ofono_info("DNS: %s, %s", dns1, dns2);
 
 	modem = ofono_gprs_context_get_modem(gc);
@@ -145,6 +182,14 @@
 
 	ofono_info("NDIS: %s", devnode);
 
+	channel = g_at_tty_open(devnode, NULL);
+	if (channel) {
+		gcd->ndis_watch = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				ndis_receive_callback, gc);
+	}
+	g_io_channel_unref(channel);
+
 	interface = "invalid";
 
 	CALLBACK_WITH_SUCCESS(gcd->up_cb, interface, TRUE, ip,
@@ -168,7 +213,7 @@
 }
 
 static void at_ndisdup_down_cb(gboolean ok, GAtResult *result,
-				gpointer user_data)
+						gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	ofono_gprs_context_cb_t cb = cbd->cb;
@@ -176,11 +221,16 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	struct ofono_error error;
 
-	DBG("");
+	DBG("ok %d", ok);
 
 	if (ok) {
 		gcd->down_cb = cb;
 		gcd->cb_data = cbd->data;
+
+		if (gcd->ndis_watch > 0) {
+			g_source_remove(gcd->ndis_watch);
+			gcd->ndis_watch = 0;
+		}
 	}
 
 	decode_at_error(&error, g_at_result_final_response(result));
@@ -188,7 +238,7 @@
 }
 
 static void at_ndisdup_up_cb(gboolean ok, GAtResult *result,
-				gpointer user_data)
+						gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	ofono_gprs_context_up_cb_t cb = cbd->cb;
@@ -196,7 +246,7 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	struct ofono_error error;
 
-	DBG("");
+	DBG("ok %d", ok);
 
 	if (ok) {
 		gcd->up_cb = cb;
@@ -223,7 +273,7 @@
 	struct cb_data *ncbd;
 	char buf[64];
 
-	DBG("");
+	DBG("ok %d", ok);
 
 	if (!ok) {
 		struct ofono_error error;
@@ -256,10 +306,10 @@
 {
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	struct cb_data *cbd = cb_data_new(cb, data);
-	char buf[AUTH_BUF_LENGTH];
+	char buf[64];
 	int len;
 
-	DBG("");
+	DBG("cid %u", ctx->cid);
 
 	if (!cbd)
 		goto error;
@@ -316,6 +366,14 @@
 {
 	GAtChat *chat = data;
 	struct gprs_context_data *gcd;
+	struct stat st;
+
+	DBG("");
+
+	if (stat(TUN_SYSFS_DIR, &st) < 0) {
+		ofono_error("Missing support for TUN/TAP devices");
+		return -ENODEV;
+	}
 
 	gcd = g_try_new0(struct gprs_context_data, 1);
 	if (!gcd)
@@ -332,6 +390,8 @@
 {
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
+	DBG("");
+
 	ofono_gprs_context_set_data(gc, NULL);
 
 	g_at_chat_unref(gcd->chat);
--- drivers/ifxmodem/gprs-context.c
+++ drivers/ifxmodem/gprs-context.c
@@ -84,6 +84,8 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	GAtIO *io;
 
+	DBG("");
+
 	io = g_at_chat_get_io(gcd->chat);
 
 	g_at_chat_suspend(gcd->chat);
@@ -110,6 +112,8 @@
 	struct ofono_error error;
 	char buf[64];
 
+	DBG("deactivate %d", deactivate);
+
 	if (deactivate == TRUE) {
 		sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
 		g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
@@ -135,6 +139,8 @@
 	const char *interface;
 	const char *dns[3];
 
+	DBG("ok %d", ok);
+
 	if (!ok) {
 		ofono_error("Failed to establish session");
 		failed_setup(gc, result, TRUE);
@@ -164,8 +170,11 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	char buf[64];
 	int cid;
-	const char *address;
+	const char *dns1, *dns2;
 	GAtResultIter iter;
+	gboolean found = FALSE;
+
+	DBG("ok %d", ok);
 
 	if (!ok) {
 		ofono_error("Unable to get DNS details");
@@ -175,25 +184,26 @@
 
 	g_at_result_iter_init(&iter, result);
 
-	if (!g_at_result_iter_next(&iter, "+XDNS:"))
-		goto error;
-
-	if (!g_at_result_iter_next_number(&iter, &cid))
-		goto error;
-
-	if ((unsigned int) cid != gcd->active_context)
-		goto error;
-
-	if (!g_at_result_iter_next_string(&iter, &address))
-		goto error;
-
-	strncpy(gcd->dns1, address, sizeof(gcd->dns1));
+	while (g_at_result_iter_next(&iter, "+XDNS:")) {
+		if (!g_at_result_iter_next_number(&iter, &cid))
+			goto error;
+
+		if (!g_at_result_iter_next_string(&iter, &dns1))
+			goto error;
+
+		if (!g_at_result_iter_next_string(&iter, &dns2))
+			goto error;
+
+		if ((unsigned int) cid == gcd->active_context) {
+			found = TRUE;
+			strncpy(gcd->dns1, dns1, sizeof(gcd->dns1));
+			strncpy(gcd->dns2, dns2, sizeof(gcd->dns2));
+		}
+	}
 
-	if (!g_at_result_iter_next_string(&iter, &address))
+	if (found == FALSE)
 		goto error;
 
-	strncpy(gcd->dns2, address, sizeof(gcd->dns2));
-
 	ofono_info("IP: %s", gcd->address);
 	ofono_info("DNS: %s, %s", gcd->dns1, gcd->dns2);
 
@@ -214,6 +224,8 @@
 	const char *address;
 	GAtResultIter iter;
 
+	DBG("ok %d", ok);
+
 	if (!ok) {
 		ofono_error("Unable to get context address");
 		failed_setup(gc, result, TRUE);
@@ -250,6 +262,8 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	char buf[64];
 
+	DBG("ok %d", ok);
+
 	if (!ok) {
 		ofono_error("Unable to activate context");
 		failed_setup(gc, result, FALSE);
@@ -268,7 +282,9 @@
 {
 	struct ofono_gprs_context *gc = user_data;
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
-	char buf[64];
+	char buf[128];
+
+	DBG("ok %d", ok);
 
 	if (!ok) {
 		ofono_error("Failed to setup context");
@@ -276,14 +292,25 @@
 		return;
 	}
 
+	if (gcd->username[0] && gcd->password[0])
+		sprintf(buf, "AT+XGAUTH=%u,1,\"%s\",\"%s\"",
+			gcd->active_context, gcd->username, gcd->password);
+	else
+		sprintf(buf, "AT+XGAUTH=%u,0,\"\",\"\"", gcd->active_context);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+		goto error;
+
 	sprintf(buf, "AT+XDNS=%u,1", gcd->active_context);
-	g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+		goto error;
 
 	sprintf(buf, "AT+CGACT=1,%u", gcd->active_context);
 	if (g_at_chat_send(gcd->chat, buf, none_prefix,
 				activate_cb, gc, NULL) > 0)
 		return;
 
+error:
 	failed_setup(gc, NULL, FALSE);
 }
 
@@ -295,6 +322,8 @@
 	char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
 	int len;
 
+	DBG("cid %u", ctx->cid);
+
 	gcd->active_context = ctx->cid;
 	gcd->up_cb = cb;
 	gcd->cb_data = data;
@@ -321,6 +350,8 @@
 	struct ofono_gprs_context *gc = user_data;
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
+	DBG("ok %d", ok);
+
 	g_at_rawip_unref(gcd->rawip);
 	gcd->rawip = NULL;
 
@@ -333,14 +364,14 @@
 }
 
 static void ifx_gprs_deactivate_primary(struct ofono_gprs_context *gc,
-					unsigned int id,
+					unsigned int cid,
 					ofono_gprs_context_cb_t cb, void *data)
 {
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	GAtChat *chat = g_at_chat_get_slave(gcd->chat);
 	char buf[64];
 
-	DBG("");
+	DBG("cid %u", cid);
 
 	gcd->state = STATE_DISABLING;
 	gcd->down_cb = cb;
@@ -381,18 +412,24 @@
 	if (!g_at_result_iter_next_number(&iter, &cid))
 		return;
 
+	DBG("cid %d", cid);
+
 	if ((unsigned int) cid != gcd->active_context)
 		return;
 
 	if (gcd->state != STATE_IDLE && gcd->rawip) {
+		g_at_rawip_shutdown(gcd->rawip);
+
 		g_at_rawip_unref(gcd->rawip);
 		gcd->rawip = NULL;
+	}
 
-		gcd->active_context = 0;
-		gcd->state = STATE_IDLE;
+	ofono_gprs_context_deactivated(gc, gcd->active_context);
 
-		g_at_chat_resume(gcd->chat);
-	}
+	gcd->active_context = 0;
+	gcd->state = STATE_IDLE;
+
+	g_at_chat_resume(gcd->chat);
 }
 
 static int ifx_gprs_context_probe(struct ofono_gprs_context *gc,
@@ -402,6 +439,8 @@
 	struct gprs_context_data *gcd;
 	struct stat st;
 
+	DBG("");
+
 	if (stat(TUN_SYSFS_DIR, &st) < 0) {
 		ofono_error("Missing support for TUN/TAP devices");
 		return -ENODEV;
--- drivers/ifxmodem/radio-settings.c
+++ drivers/ifxmodem/radio-settings.c
@@ -129,7 +129,7 @@
 	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
 	struct cb_data *cbd = cb_data_new(cb, data);
 	char buf[20];
-	int value, preferred = 2;
+	int value = 1, preferred = 2;
 
 	switch (mode) {
 	case OFONO_RADIO_ACCESS_MODE_ANY:
@@ -141,19 +141,19 @@
 	case OFONO_RADIO_ACCESS_MODE_UMTS:
 		value = 2;
 		break;
-	default:
-		CALLBACK_WITH_FAILURE(cb, data);
-		g_free(cbd);
-		return;
+	case OFONO_RADIO_ACCESS_MODE_LTE:
+		goto error;
 	}
 
 	snprintf(buf, sizeof(buf), "AT+XRAT=%u,%u", value, preferred);
 
 	if (g_at_chat_send(rsd->chat, buf, none_prefix,
-					xrat_modify_cb, cbd, g_free) == 0) {
-		CALLBACK_WITH_FAILURE(cb, data);
-		g_free(cbd);
-	}
+					xrat_modify_cb, cbd, g_free) > 0)
+		return;
+
+error:
+	CALLBACK_WITH_FAILURE(cb, data);
+	g_free(cbd);
 }
 
 static void xrat_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
--- drivers/ifxmodem/stk.c
+++ drivers/ifxmodem/stk.c
@@ -197,12 +197,14 @@
 
 static void satn_notify(GAtResult *result, gpointer user_data)
 {
+	struct ofono_stk *stk = user_data;
 	GAtResultIter iter;
 	const guint8 *pdu;
 	gint len;
 
 	DBG("");
 
+	/* Proactive command has been handled by the modem. */
 	g_at_result_iter_init(&iter, result);
 
 	if (g_at_result_iter_next(&iter, "+SATN:") == FALSE)
@@ -211,12 +213,10 @@
 	if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE)
 		return;
 
-	DBG("len %d", len);
+	if (len == 0)
+		return;
 
-	/* Proactive command has been handled by the modem.  If the
-	 * command was for Setup Call then a response with AT+SATD
-	 * is required.  This is not handled properly yet.
-	 */
+	ofono_stk_proactive_command_handled_notify(stk, len, pdu);
 }
 
 static void satf_notify(GAtResult *result, gpointer user_data)
--- drivers/ifxmodem/voicecall.c
+++ drivers/ifxmodem/voicecall.c
@@ -187,6 +187,33 @@
 		ofono_voicecall_notify(vc, call);
 }
 
+static void xem_notify(GAtResult *result, gpointer user_data)
+{
+	//struct ofono_voicecall *vc = user_data;
+	//struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+	GAtResultIter iter;
+	int state;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (g_at_result_iter_next(&iter, "+XEM:") == FALSE)
+		return;
+
+	if (g_at_result_iter_next_number(&iter, &state) == FALSE)
+		return;
+
+	DBG("state %d", state);
+
+	switch (state) {
+	case 0:
+		ofono_info("Emergency call is finished");
+		break;
+	case 1:
+		ofono_info("Emergency call is entered");
+		break;
+	}
+}
+
 static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct change_state_req *req = user_data;
@@ -682,8 +709,9 @@
 	g_at_chat_register(vd->chat, "+CRING:", cring_notify, FALSE, vc, NULL);
 	g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL);
 	g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL);
+	g_at_chat_register(vd->chat, "+XEM:", xem_notify, FALSE, vc, NULL);
 	g_at_chat_register(vd->chat, "+XCALLSTAT:", xcallstat_notify,
-				FALSE, vc, NULL);
+							FALSE, vc, NULL);
 
 	ofono_voicecall_register(vc);
 }
@@ -703,10 +731,12 @@
 	ofono_voicecall_set_data(vc, vd);
 
 	g_at_chat_send(chat, "AT+XCALLSTAT=1", none_prefix, NULL, NULL, NULL);
+	g_at_chat_send(chat, "AT+XEMC=1", none_prefix, NULL, NULL, NULL);
 
 	g_at_chat_send(vd->chat, "AT+CRC=1", none_prefix, NULL, NULL, NULL);
 	g_at_chat_send(vd->chat, "AT+CLIP=1", none_prefix, NULL, NULL, NULL);
 	g_at_chat_send(vd->chat, "AT+COLP=1", none_prefix, NULL, NULL, NULL);
+	g_at_chat_send(vd->chat, "AT+CNAP=1", none_prefix, NULL, NULL, NULL);
 	g_at_chat_send(vd->chat, "AT+CCWA=1", none_prefix,
 				ifx_voicecall_initialized, vc, NULL);
 
--- drivers/isimodem/gprs-context.c
+++ drivers/isimodem/gprs-context.c
@@ -50,22 +50,20 @@
 #include "debug.h"
 
 #define STATIC_IP_NETMASK "255.255.255.255"
+#define ACTIVATE_TIMEOUT	(6 * 30)	/* 6 * T3380 */
+#define DEACTIVATE_TIMEOUT	(6 * 8)		/* 6 * T3390 */
 
 #define INVALID_ID (0xff)
 # if (INVALID_ID < GPDS_MAX_CONTEXT_COUNT)
 #   error Uho! This should not happen!
 #endif
 
-struct gprs_context_data {
+struct context_data {
 	GIsiClient *client;
 	GIsiModem *idx;
 	uint16_t gpds;	/* GPDS object handle */
-	GSList *contexts;
-};
-
-struct context_data {
 	unsigned cid;	/* oFono core context ID */
-	struct ofono_gprs_context *driver;
+	struct ofono_gprs_context *context;
 	union {
 		ofono_gprs_context_up_cb_t up_cb;
 		ofono_gprs_context_cb_t down_cb;
@@ -74,6 +72,8 @@
 
 	GIsiPEP *pep;
 	GIsiPipe *pipe;
+	guint activate_timeout;
+	guint deactivate_timeout;
 
 	char apn[GPDS_MAX_APN_STRING_LENGTH + 1];
 	char username[GPDS_MAX_USERNAME_LENGTH + 1];
@@ -83,40 +83,24 @@
 	uint8_t type;
 };
 
-static struct context_data *find_context_by_cid(GSList *contexts,
-						unsigned int cid)
-{
-	GSList *m = NULL;
-
-	for (m = contexts; m; m = m->next) {
-		struct context_data *cd = m->data;
-
-		if (cd->cid == cid)
-			return cd;
-	}
-	return NULL;
-}
-
-static struct context_data *find_context_by_handle(GSList *contexts,
-							uint8_t handle)
-{
-	GSList *m = NULL;
-
-	for (m = contexts; m; m = m->next) {
-		struct context_data *cd = m->data;
-
-		if (cd->handle == handle)
-			return cd;
-	}
-	return NULL;
-}
-
-static void destroy_context(struct context_data *cd)
+static void reset_context(struct context_data *cd)
 {
 	if (!cd)
 		return;
 
-	DBG("destroying %p (cid=%u)", cd, cd->cid);
+	g_isi_remove_subscription(cd->client, PN_GPDS,
+				GPDS_CONTEXT_ACTIVATE_IND);
+	g_isi_remove_subscription(cd->client,
+				PN_GPDS, GPDS_CONTEXT_ACTIVATE_FAIL_IND);
+	g_isi_remove_subscription(cd->client,
+				PN_GPDS, GPDS_CONTEXT_DEACTIVATE_IND);
+	g_isi_commit_subscriptions(cd->client);
+
+	if (cd->activate_timeout)
+		g_source_remove(cd->activate_timeout);
+
+	if (cd->deactivate_timeout)
+		g_source_remove(cd->deactivate_timeout);
 
 	if (cd->pipe)
 		g_isi_pipe_destroy(cd->pipe);
@@ -124,34 +108,48 @@
 	if (cd->pep)
 		g_isi_pep_destroy(cd->pep);
 
-	g_free(cd);
+	cd->activate_timeout = 0;
+	cd->deactivate_timeout = 0;
+	cd->pep = NULL;
+	cd->pipe = NULL;
+	cd->handle = INVALID_ID;
 }
 
 static gboolean gprs_up_fail(struct context_data *cd)
 {
-	struct ofono_gprs_context *gc = cd->driver;
-	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
-
 	CALLBACK_WITH_FAILURE(cd->up_cb, NULL, 0, NULL, NULL, NULL, NULL,
 				cd->data);
 
-	gcd->contexts = g_slist_remove(gcd->contexts, cd);
-	destroy_context(cd);
+	reset_context(cd);
 	return TRUE;
 }
 
 static gboolean gprs_down_fail(struct context_data *cd)
 {
-	struct ofono_gprs_context *gc = cd->driver;
-	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
-
 	CALLBACK_WITH_FAILURE(cd->down_cb, cd->data);
 
-	gcd->contexts = g_slist_remove(gcd->contexts, cd);
-	destroy_context(cd);
+	reset_context(cd);
 	return TRUE;
 }
 
+static gboolean gprs_up_timeout(gpointer data)
+{
+	struct context_data *cd = data;
+
+	cd->activate_timeout = 0;
+	gprs_up_fail(cd);
+	return FALSE;
+}
+
+static gboolean gprs_down_timeout(gpointer data)
+{
+	struct context_data *cd = data;
+
+	cd->deactivate_timeout = 0;
+	gprs_down_fail(cd);
+	return FALSE;
+}
+
 static gboolean check_resp(GIsiClient *client,
 				const uint8_t *restrict msg, size_t len,
 				uint_fast8_t cmd, struct context_data *cd)
@@ -173,7 +171,7 @@
 	}
 
 	if ((cd->handle != INVALID_ID && msg[1] != cd->handle)
-		|| (msg[1] == INVALID_ID)) {
+			|| (msg[1] == INVALID_ID)) {
 		DBG("invalid context ID: 0x%02"PRIx8, msg[1]);
 		return FALSE;
 	}
@@ -191,12 +189,29 @@
 	return TRUE;
 }
 
+static void deactivate_ind_cb(GIsiClient *client,
+				const void *restrict data, size_t len,
+				uint16_t object, void *opaque)
+{
+	struct context_data *cd = opaque;
+	const unsigned char *msg = data;
+
+	if (!msg || len < 3 || msg[0] != GPDS_CONTEXT_DEACTIVATE_IND ||
+			msg[1] != cd->handle)
+		return;
+
+	DBG("context deactivated: %s (0x%02"PRIx8")",
+		gpds_isi_cause_name(msg[3]), msg[3]);
+
+	ofono_gprs_context_deactivated(cd->context, cd->cid);
+	reset_context(cd);
+}
+
 static void activate_ind_cb(GIsiClient *client,
 				const void *restrict data, size_t len,
 				uint16_t object, void *opaque)
 {
-	struct gprs_context_data *gcd = opaque;
-	struct context_data *cd;
+	struct context_data *cd = opaque;
 
 	const unsigned char *msg = data;
 	GIsiSubBlockIter iter;
@@ -207,18 +222,13 @@
 	char *sdns = NULL;
 	const char *dns[3];
 
-	if (!msg || len < 3 || msg[0] != GPDS_CONTEXT_ACTIVATE_IND)
-		return;
-
-	cd = find_context_by_handle(gcd->contexts, msg[1]);
-	if (!cd) {
-		DBG("unknown context: 0x%02"PRIx8, msg[1]);
+	if (!msg || len < 3 || msg[0] != GPDS_CONTEXT_ACTIVATE_IND ||
+			msg[1] != cd->handle)
 		return;
-	}
 
 	for (g_isi_sb_iter_init(&iter, msg, len, 3);
-		g_isi_sb_iter_is_valid(&iter);
-		g_isi_sb_iter_next(&iter)) {
+			g_isi_sb_iter_is_valid(&iter);
+			g_isi_sb_iter_next(&iter)) {
 
 		uint8_t *addr_value = NULL;
 		uint8_t addr_len = 0;
@@ -228,9 +238,12 @@
 		/* TODO: IPv6 address support */
 
 		case GPDS_PDP_ADDRESS_INFO:
-			if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3)
-				|| !g_isi_sb_iter_get_data(&iter,
-						(void *)&addr_value, 4))
+
+			if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3))
+				goto error;
+
+			if (!g_isi_sb_iter_get_data(&iter, (void *)&addr_value,
+							4))
 				goto error;
 
 			ip = alloca(INET_ADDRSTRLEN);
@@ -239,9 +252,12 @@
 			break;
 
 		case GPDS_PDNS_ADDRESS_INFO:
-			if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3)
-				|| !g_isi_sb_iter_get_data(&iter,
-						(void *)&addr_value, 4))
+
+			if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3))
+				break;
+
+			if (!g_isi_sb_iter_get_data(&iter, (void *)&addr_value,
+							4))
 				break;
 
 			pdns = alloca(INET_ADDRSTRLEN);
@@ -250,9 +266,12 @@
 			break;
 
 		case GPDS_SDNS_ADDRESS_INFO:
-			if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3)
-				|| !g_isi_sb_iter_get_data(&iter,
-						(void *)&addr_value, 4))
+
+			if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3))
+				break;
+
+			if (!g_isi_sb_iter_get_data(&iter, (void *)&addr_value,
+							4))
 				break;
 
 			sdns = alloca(INET_ADDRSTRLEN);
@@ -277,6 +296,9 @@
 	CALLBACK_WITH_SUCCESS(cd->up_cb, ifname, TRUE, (const char *)ip,
 					STATIC_IP_NETMASK, NULL,
 					dns, cd->data);
+
+	g_source_remove(cd->activate_timeout);
+	cd->activate_timeout = 0;
 	return;
 
 error:
@@ -288,18 +310,11 @@
 					uint16_t object, void *opaque)
 {
 	const unsigned char *msg = data;
-	struct gprs_context_data *gcd = opaque;
-	struct context_data *cd;
+	struct context_data *cd = opaque;
 
 	if (!msg || len < 3 || msg[0] != GPDS_CONTEXT_ACTIVATE_FAIL_IND)
 		return;
 
-	cd = find_context_by_handle(gcd->contexts, msg[1]);
-	if (cd == NULL) {
-		DBG("unknown context: 0x%02"PRIx8, msg[1]);
-		return;
-	}
-
 	gprs_up_fail(cd);
 }
 
@@ -312,8 +327,6 @@
 	if (!check_resp(client, data, len, GPDS_CONTEXT_ACTIVATE_RESP, cd))
 		return gprs_up_fail(cd);
 
-	/* TODO: Add timeout here in case indications never come */
-
 	return TRUE;
 }
 
@@ -327,6 +340,15 @@
 		0,		/* sub blocks */
 	};
 
+
+	g_isi_add_subscription(client, PN_GPDS, GPDS_CONTEXT_ACTIVATE_IND,
+				activate_ind_cb, cd);
+	g_isi_add_subscription(client, PN_GPDS, GPDS_CONTEXT_ACTIVATE_FAIL_IND,
+				activate_fail_ind_cb, cd);
+	g_isi_add_subscription(client, PN_GPDS, GPDS_CONTEXT_DEACTIVATE_IND,
+				deactivate_ind_cb, cd);
+	g_isi_commit_subscriptions(client);
+
 	if (g_isi_request_make(client, msg, sizeof(msg), GPDS_TIMEOUT,
 				context_activate_cb, cd))
 		g_isi_pipe_start(cd->pipe);
@@ -468,58 +490,30 @@
 static void create_pipe_cb(GIsiPipe *pipe)
 {
 	struct context_data *cd = g_isi_pipe_get_userdata(pipe);
-	struct ofono_gprs_context *gc = cd->driver;
-	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
 	const unsigned char msg[] = {
 		GPDS_CONTEXT_ID_CREATE_REQ,
 	};
 
-	if (!g_isi_request_make(gcd->client, msg, sizeof(msg), GPDS_TIMEOUT,
+	if (!g_isi_request_make(cd->client, msg, sizeof(msg), GPDS_TIMEOUT,
 				create_context_cb, cd))
 		gprs_up_fail(cd);
 }
 
-static void deactivate_ind_cb(GIsiClient *client,
-				const void *restrict data, size_t len,
-				uint16_t object, void *opaque)
-{
-	struct gprs_context_data *gcd = opaque;
-	struct context_data *cd;
-
-	const unsigned char *msg = data;
-
-	if (!msg || len < 3 || msg[0] != GPDS_CONTEXT_DEACTIVATE_IND)
-		return;
-
-	cd = find_context_by_handle(gcd->contexts, msg[1]);
-	if (cd == NULL) {
-		DBG("unknown context: 0x%02"PRIx8, msg[1]);
-		return;
-	}
-
-	DBG("context deactivated: %s (0x%02"PRIx8")",
-		gpds_isi_cause_name(msg[3]), msg[3]);
-
-	ofono_gprs_context_deactivated(cd->driver, cd->cid);
-
-	gcd->contexts = g_slist_remove(gcd->contexts, cd);
-	destroy_context(cd);
-}
-
 static void isi_gprs_activate_primary(struct ofono_gprs_context *gc,
 				const struct ofono_gprs_primary_context *ctx,
 				ofono_gprs_context_up_cb_t cb, void *data)
 {
-	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
-	struct context_data *cd = g_try_new0(struct context_data, 1);
-	struct context_data *old = NULL;
+	struct context_data *cd = ofono_gprs_context_get_data(gc);
 
-	if (!gcd || !cd)
+	if (!cd->gpds) {
+		/* GPDS is not reachable */
+		CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL, NULL,
+					NULL, data);
 		return;
+	}
 
 	cd->cid = ctx->cid;
-	cd->driver = gc;
 	cd->up_cb = cb;
 	cd->data = data;
 	cd->pep = NULL;
@@ -527,17 +521,9 @@
 	cd->handle = INVALID_ID;
 	cd->type = GPDS_PDP_TYPE_IPV4;
 
-	old = find_context_by_cid(gcd->contexts, ctx->cid);
-	if (old) {
-		DBG("duplicate context: %u", ctx->cid);
-		goto error;
-	}
-
-	gcd->contexts = g_slist_append(gcd->contexts, cd);
-
 	if (strlen(ctx->apn) >= GPDS_MAX_APN_STRING_LENGTH
-		|| strlen(ctx->username) >= GPDS_MAX_USERNAME_LENGTH
-		|| strlen(ctx->password) >= GPDS_MAX_PASSWORD_LENGTH)
+			|| strlen(ctx->username) >= GPDS_MAX_USERNAME_LENGTH
+			|| strlen(ctx->password) >= GPDS_MAX_PASSWORD_LENGTH)
 		goto error;
 
 	strncpy(cd->apn, ctx->apn, GPDS_MAX_APN_STRING_LENGTH);
@@ -549,18 +535,20 @@
 	strncpy(cd->password, ctx->password, GPDS_MAX_PASSWORD_LENGTH);
 	cd->username[GPDS_MAX_PASSWORD_LENGTH] = '\0';
 
-	cd->pep = g_isi_pep_create(gcd->idx, NULL, NULL);
+	cd->pep = g_isi_pep_create(cd->idx, NULL, NULL);
 	if (cd->pep == NULL)
 		goto error;
 
-	cd->pipe = g_isi_pipe_create(gcd->idx, create_pipe_cb,
+	cd->pipe = g_isi_pipe_create(cd->idx, create_pipe_cb,
 					g_isi_pep_get_object(cd->pep),
-					gcd->gpds, PN_PEP_TYPE_GPRS,
+					cd->gpds, PN_PEP_TYPE_GPRS,
 					PN_PEP_TYPE_GPRS);
 	if (cd->pipe == NULL)
 		goto error;
 
 	g_isi_pipe_set_userdata(cd->pipe, cd);
+	cd->activate_timeout = g_timeout_add_seconds(ACTIVATE_TIMEOUT,
+							gprs_up_timeout, cd);
 	return;
 
 error:
@@ -573,16 +561,12 @@
 					void *opaque)
 {
 	struct context_data *cd = opaque;
-	struct ofono_gprs_context *gc = cd->driver;
-	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
 	if (!check_resp(client, data, len, GPDS_CONTEXT_DEACTIVATE_RESP, cd))
 		return gprs_down_fail(cd);
 
-	gcd->contexts = g_slist_remove(gcd->contexts, cd);
-
 	CALLBACK_WITH_SUCCESS(cd->down_cb, cd->data);
-	destroy_context(cd);
+	reset_context(cd);
 
 	return TRUE;
 }
@@ -591,39 +575,36 @@
 					unsigned int cid,
 					ofono_gprs_context_cb_t cb, void *data)
 {
-	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
-	struct context_data *cd;
+	struct context_data *cd = ofono_gprs_context_get_data(gc);
 
 	unsigned char msg[] = {
 		GPDS_CONTEXT_DEACTIVATE_REQ,
 		0x00,	/* GPDS context ID, added later */
 	};
 
-	if (!gcd)
-		return;
-
-	cd = find_context_by_cid(gcd->contexts, cid);
-	if (!cd) {
-		DBG("unknown context: %u", cid);
+	if (!cd)
 		return;
-	}
 
 	cd->down_cb = cb;
 	cd->data = data;
 
 	msg[1] = cd->handle;
 
-	if (!g_isi_request_make(gcd->client, msg, sizeof(msg), GPDS_TIMEOUT,
-				context_deactivate_cb, cd))
+	if (!g_isi_request_make(cd->client, msg, sizeof(msg), GPDS_TIMEOUT,
+					context_deactivate_cb, cd)) {
 		gprs_down_fail(cd);
+		return;
+	}
+
+	cd->deactivate_timeout = g_timeout_add_seconds(DEACTIVATE_TIMEOUT,
+							gprs_down_timeout, cd);
 }
 
 static void gpds_ctx_reachable_cb(GIsiClient *client, gboolean alive,
 					uint16_t object,
 					void *opaque)
 {
-	struct ofono_gprs_context *gc = opaque;
-	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct context_data *cd = opaque;
 	const char *debug;
 
 	if (!alive) {
@@ -636,61 +617,51 @@
 		g_isi_version_major(client),
 		g_isi_version_minor(client));
 
-	gcd->gpds = object;
+	cd->gpds = object;
 
 	debug = getenv("OFONO_ISI_DEBUG");
 	if (debug && (strcmp(debug, "all") == 0 || strcmp(debug, "gpds") == 0))
-		g_isi_client_set_debug(gcd->client, gpds_debug, NULL);
-
-	g_isi_subscribe(client, GPDS_CONTEXT_ACTIVATE_IND,
-			activate_ind_cb, gcd);
-	g_isi_subscribe(client, GPDS_CONTEXT_ACTIVATE_FAIL_IND,
-			activate_fail_ind_cb, gcd);
-	g_isi_subscribe(client, GPDS_CONTEXT_DEACTIVATE_IND,
-			deactivate_ind_cb, gcd);
+		g_isi_client_set_debug(cd->client, gpds_debug, NULL);
 }
 
 static int isi_gprs_context_probe(struct ofono_gprs_context *gc,
 					unsigned int vendor, void *user)
 {
 	GIsiModem *idx = user;
-	struct gprs_context_data *gcd = g_try_new0(struct gprs_context_data, 1);
+	struct context_data *cd = g_try_new0(struct context_data, 1);
 
-	if (!gcd)
+	if (!cd)
 		return -ENOMEM;
 
-	gcd->client = g_isi_client_create(idx, PN_GPDS);
-	if (!gcd->client) {
-		g_free(gcd);
+	cd->client = g_isi_client_create(idx, PN_GPDS);
+	if (!cd->client) {
+		g_free(cd);
 		return -ENOMEM;
 	}
 
-	ofono_gprs_context_set_data(gc, gcd);
-
-	gcd->idx = idx;
-	gcd->contexts = NULL;
+	cd->idx = idx;
+	cd->context = gc;
+	ofono_gprs_context_set_data(gc, cd);
 
-	g_isi_verify(gcd->client, gpds_ctx_reachable_cb, gc);
+	g_isi_verify(cd->client, gpds_ctx_reachable_cb, cd);
 
 	return 0;
 }
 
 static void isi_gprs_context_remove(struct ofono_gprs_context *gc)
 {
-	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
-	GSList *m;
+	struct context_data *cd = ofono_gprs_context_get_data(gc);
 
-	ofono_gprs_context_set_data(gc, NULL);
-
-	for (m = gcd->contexts; m; m = m->next)
-		destroy_context(m->data);
+	if (!cd)
+		return;
 
-	g_slist_free(gcd->contexts);
+	ofono_gprs_context_set_data(gc, NULL);
+	reset_context(cd);
 
-	if (gcd->client)
-		g_isi_client_destroy(gcd->client);
+	if (cd->client)
+		g_isi_client_destroy(cd->client);
 
-	g_free(gcd);
+	g_free(cd);
 }
 
 static struct ofono_gprs_context_driver driver = {
--- drivers/isimodem/mtc.h
+++ drivers/isimodem/mtc.h
@@ -28,6 +28,7 @@
 
 #define PN_MTC			0x15
 #define MTC_TIMEOUT		5
+#define MTC_STATE_REQ_TIMEOUT   (6 + 5)
 
 enum mtc_isi_cause {
 	MTC_OK = 0x00,
--- drivers/isimodem/radio-settings.c
+++ drivers/isimodem/radio-settings.c
@@ -41,11 +41,16 @@
 #include "isimodem.h"
 #include "isiutil.h"
 #include "debug.h"
+#include "gpds.h"
 #include "gss.h"
 #include "network.h"
 
+#define PN_WRAN 0xb4
+
 struct radio_data {
 	GIsiClient *client;
+	uint16_t wran_object;
+	uint16_t quick_release:1;
 };
 
 static enum ofono_radio_access_mode isi_mode_to_ofono_mode(guint8 mode)
@@ -236,6 +241,65 @@
 	g_free(cbd);
 }
 
+static void update_fast_dormancy(struct radio_data *rd)
+{
+	struct sockaddr_pn dst = {
+		.spn_family = AF_PHONET,
+		.spn_resource = 0x3a,
+		.spn_dev = rd->wran_object >> 8,
+		.spn_obj = rd->wran_object & 0xff,
+	};
+
+	if (!rd->wran_object)
+		return;
+
+	if (rd->quick_release) {
+		const unsigned char msg[] = {
+			0x1f, 0x00, 0x01, 0x01, 0x01, 0x00
+		};
+
+		g_isi_sendto(rd->client, &dst, msg, sizeof(msg), 0,
+				NULL, NULL, NULL);
+	} else {
+		const unsigned char msg[] = {
+			0x1f, 0x00, 0x01, 0x01, 0x02, 0x0a
+		};
+
+		g_isi_sendto(rd->client, &dst, msg, sizeof(msg), 0,
+				NULL, NULL, NULL);
+	}
+
+	DBG("3G PS quick release %s",
+		rd->quick_release ? "enabled" : "disabled");
+}
+
+static void gpds_context_activating_ind_cb(GIsiClient *client,
+					const void *restrict data, size_t len,
+					uint16_t object, void *opaque)
+{
+	struct radio_data *rd = opaque;
+	update_fast_dormancy(rd);
+}
+
+static void isi_query_fast_dormancy(struct ofono_radio_settings *rs,
+			ofono_radio_settings_fast_dormancy_query_cb_t cb,
+			void *data)
+{
+	struct radio_data *rd = ofono_radio_settings_get_data(rs);
+	CALLBACK_WITH_SUCCESS(cb, rd->quick_release, data);
+}
+
+static void isi_set_fast_dormancy(struct ofono_radio_settings *rs,
+				int enable,
+				ofono_radio_settings_fast_dormancy_set_cb_t cb,
+				void *data)
+{
+	struct radio_data *rd = ofono_radio_settings_get_data(rs);
+	rd->quick_release = enable;
+	update_fast_dormancy(rd);
+	CALLBACK_WITH_SUCCESS(cb, data);
+}
+
 static gboolean isi_radio_settings_register(gpointer user)
 {
 	struct ofono_radio_settings *rs = user;
@@ -249,9 +313,31 @@
 
 	ofono_radio_settings_register(rs);
 
+	g_isi_add_subscription(rd->client,
+				PN_GPDS, GPDS_CONTEXT_ACTIVATING_IND,
+				gpds_context_activating_ind_cb, rd);
+	g_isi_commit_subscriptions(rd->client);
+
 	return FALSE;
 }
 
+static void wran_reachable_cb(GIsiClient *client, gboolean alive,
+				uint16_t object, void *opaque)
+{
+	struct radio_data *rd = opaque;
+
+	if (!alive) {
+		DBG("fast dormancy support disabled");
+		return;
+	}
+
+	rd->wran_object = object;
+
+	DBG("PN_WRAN reachable, object=0x%04x", object);
+
+	update_fast_dormancy(rd);
+}
+
 static void reachable_cb(GIsiClient *client, gboolean alive, uint16_t object,
 				void *opaque)
 {
@@ -289,6 +375,7 @@
 	ofono_radio_settings_set_data(rs, rd);
 
 	g_isi_verify(rd->client, reachable_cb, rs);
+	g_isi_verify_resource(rd->client, PN_WRAN, wran_reachable_cb, rd);
 
 	return 0;
 }
@@ -310,7 +397,9 @@
 	.probe			= isi_radio_settings_probe,
 	.remove			= isi_radio_settings_remove,
 	.query_rat_mode		= isi_query_rat_mode,
-	.set_rat_mode		= isi_set_rat_mode
+	.set_rat_mode		= isi_set_rat_mode,
+	.query_fast_dormancy	= isi_query_fast_dormancy,
+	.set_fast_dormancy	= isi_set_fast_dormancy,
 };
 
 void isi_radio_settings_init()
--- drivers/isimodem/voicecall.c
+++ drivers/isimodem/voicecall.c
@@ -1149,6 +1149,7 @@
 		uint8_t cause = CALL_CAUSE_RELEASE_BY_USER;
 
 		switch (status->status) {
+		case CALL_STATUS_COMING:
 		case CALL_STATUS_MT_ALERTING:
 		case CALL_STATUS_WAITING:
 			cause = CALL_CAUSE_BUSY_USER_REQUEST;
--- drivers/mbmmodem/gprs-context.c
+++ drivers/mbmmodem/gprs-context.c
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <errno.h>
 
 #include <glib.h>
 
@@ -92,6 +93,8 @@
 	const char *interface;
 	gboolean success = FALSE;
 
+	DBG("ok %d", ok);
+
 	if (!ok)
 		goto out;
 
@@ -135,6 +138,9 @@
 	modem = ofono_gprs_context_get_modem(gc);
 	interface = ofono_modem_get_string(modem, "NetworkInterface");
 
+	ofono_info("IP: %s  Gateway: %s", ip, gateway);
+	ofono_info("DNS: %s, %s", dns[0], dns[1]);
+
 	CALLBACK_WITH_SUCCESS(gcd->up_cb, interface, success, ip,
 					STATIC_IP_NETMASK, gateway,
 					success ? dns : NULL, gcd->cb_data);
@@ -148,6 +154,9 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	struct ofono_modem *modem;
 	const char *interface;
+	char buf[64];
+
+	DBG("");
 
 	if (gcd->have_e2ipcfg) {
 		g_at_chat_send(gcd->chat, "AT*E2IPCFG?", e2ipcfg_prefix,
@@ -155,6 +164,9 @@
 		return;
 	}
 
+	snprintf(buf, sizeof(buf), "AT+CGPADDR=%u", gcd->active_context);
+	g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+
 	modem = ofono_gprs_context_get_modem(gc);
 	interface = ofono_modem_get_string(modem, "NetworkInterface");
 	CALLBACK_WITH_SUCCESS(gcd->up_cb, interface, FALSE, NULL, NULL,
@@ -169,12 +181,14 @@
 {
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
+	DBG("state %d", state);
+
 	if (gcd->active_context == 0)
 		return;
 
 	switch (state) {
 	case MBM_E2NAP_DISCONNECTED:
-		DBG("MBM Context: disconnected");
+		DBG("disconnected");
 
 		if (gcd->mbm_state == MBM_DISABLING) {
 			CALLBACK_WITH_SUCCESS(gcd->down_cb, gcd->cb_data);
@@ -194,7 +208,7 @@
 		break;
 
 	case MBM_E2NAP_CONNECTED:
-		DBG("MBM Context: connected");
+		DBG("connected");
 
 		if (gcd->mbm_state == MBM_ENABLING)
 			mbm_get_ip_details(gc);
@@ -202,7 +216,7 @@
 		break;
 
 	case MBM_E2NAP_CONNECTING:
-		DBG("MBM Context: connecting");
+		DBG("connecting");
 		break;
 
 	default:
@@ -219,6 +233,8 @@
 	GAtResultIter iter;
 	int state;
 
+	DBG("ok %d", ok);
+
 	g_at_result_iter_init(&iter, result);
 
 	if (g_at_result_iter_next(&iter, "*ENAP:") == FALSE)
@@ -254,6 +270,8 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	struct ofono_error error;
 
+	DBG("ok %d", ok);
+
 	/* Now we have to wait for the unsolicited notification to arrive */
 	if (ok && gcd->enap != 0) {
 		gcd->mbm_state = MBM_DISABLING;
@@ -279,6 +297,8 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	struct ofono_error error;
 
+	DBG("ok %d", ok);
+
 	if (ok) {
 		gcd->mbm_state = MBM_ENABLING;
 		gcd->up_cb = cb;
@@ -306,6 +326,8 @@
 	struct cb_data *ncbd;
 	char buf[64];
 
+	DBG("ok %d", ok);
+
 	if (!ok) {
 		struct ofono_error error;
 
@@ -340,6 +362,8 @@
 	char buf[AUTH_BUF_LENGTH];
 	int len;
 
+	DBG("cid %u", ctx->cid);
+
 	if (!cbd)
 		goto error;
 
@@ -382,6 +406,8 @@
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 	struct cb_data *cbd = cb_data_new(cb, data);
 
+	DBG("cid %u", cid);
+
 	if (!cbd)
 		goto error;
 
@@ -418,6 +444,8 @@
 	struct ofono_gprs_context *gc = user_data;
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
+	DBG("ok %d", ok);
+
 	gcd->have_e2nap = ok;
 
 	if (ok)
@@ -440,7 +468,12 @@
 	GAtChat *chat = data;
 	struct gprs_context_data *gcd;
 
-	gcd = g_new0(struct gprs_context_data, 1);
+	DBG("");
+
+	gcd = g_try_new0(struct gprs_context_data, 1);
+	if (!gcd)
+		return -ENOMEM;
+
 	gcd->chat = g_at_chat_clone(chat);
 
 	ofono_gprs_context_set_data(gc, gcd);
@@ -460,6 +493,8 @@
 {
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
+	DBG("");
+
 	if (gcd->enap_source) {
 		g_source_remove(gcd->enap_source);
 		gcd->enap_source = 0;
--- drivers/mbmmodem/stk.c
+++ drivers/mbmmodem/stk.c
@@ -179,12 +179,26 @@
 
 static void stkn_notify(GAtResult *result, gpointer user_data)
 {
+	struct ofono_stk *stk = user_data;
+	GAtResultIter iter;
+	const guint8 *pdu;
+	gint len;
+
 	DBG("");
 
-	/* Proactive command has been handled by the modem.  Should
-	 * the core be notified?  For now we just ignore it because
-	 * we must not respond to the command.
-	 */
+	/* Proactive command has been handled by the modem. */
+	g_at_result_iter_init(&iter, result);
+
+	if (g_at_result_iter_next(&iter, "*STKN:") == FALSE)
+		return;
+
+	if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE)
+		return;
+
+	if (len == 0)
+		return;
+
+	ofono_stk_proactive_command_handled_notify(stk, len, pdu);
 }
 
 static void stkend_notify(GAtResult *result, gpointer user_data)
--- drivers/nwmodem/radio-settings.c
+++ drivers/nwmodem/radio-settings.c
@@ -126,7 +126,7 @@
 	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
 	struct cb_data *cbd = cb_data_new(cb, data);
 	char buf[20];
-	int value;
+	int value = 0;
 
 	switch (mode) {
 	case OFONO_RADIO_ACCESS_MODE_ANY:
@@ -138,19 +138,19 @@
 	case OFONO_RADIO_ACCESS_MODE_UMTS:
 		value = 2;
 		break;
-	default:
-		CALLBACK_WITH_FAILURE(cb, data);
-		g_free(cbd);
-		return;
+	case OFONO_RADIO_ACCESS_MODE_LTE:
+		goto error;
 	}
 
 	snprintf(buf, sizeof(buf), "AT$NWRAT=%u,2", value);
 
 	if (g_at_chat_send(rsd->chat, buf, none_prefix,
-					nwrat_modify_cb, cbd, g_free) == 0) {
-		CALLBACK_WITH_FAILURE(cb, data);
-		g_free(cbd);
-	}
+					nwrat_modify_cb, cbd, g_free) > 0)
+		return;
+
+error:
+	CALLBACK_WITH_FAILURE(cb, data);
+	g_free(cbd);
 }
 
 static void nwrat_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
--- drivers/stemodem/caif_rtnl.c
+++ drivers/stemodem/caif_rtnl.c
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010 ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <fcntl.h>
+#include <linux/rtnetlink.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+
+#include "if_caif.h"
+#include "caif_rtnl.h"
+
+#define NLMSG_TAIL(nmsg) \
+	((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#define RTNL_MSG_SIZE 1024
+
+struct rtnl_msg {
+	struct nlmsghdr n;
+	struct ifinfomsg i;
+	char data[RTNL_MSG_SIZE];
+};
+
+struct iplink_req {
+	__u32 rtnlmsg_seqnr;
+	void *user_data;
+	caif_rtnl_create_cb_t callback;
+};
+
+static GSList *pending_requests;
+static __u32 rtnl_seqnr;
+static guint rtnl_watch;
+static GIOChannel *rtnl_channel;
+
+static struct iplink_req *find_request(__u32 seq)
+{
+	GSList *list;
+
+	for (list = pending_requests; list; list = list->next) {
+		struct iplink_req *req = list->data;
+
+		if (req->rtnlmsg_seqnr == seq)
+			return req;
+	}
+
+	return NULL;
+}
+
+static void parse_newlink_param(struct ifinfomsg *msg, int size,
+						int *index, char *ifname)
+{
+	struct rtattr *attr;
+
+	for (attr = IFLA_RTA(msg); RTA_OK(attr, size);
+		attr = RTA_NEXT(attr, size)) {
+
+		if (attr->rta_type == IFLA_IFNAME &&
+				ifname != NULL) {
+
+			strncpy(ifname, RTA_DATA(attr), IF_NAMESIZE);
+			ifname[IF_NAMESIZE-1] = '\0';
+			break;
+		}
+	}
+
+	*index = msg->ifi_index;
+}
+
+static void parse_rtnl_message(const void *buf, size_t len)
+{
+	struct ifinfomsg *msg;
+	struct iplink_req *req;
+	char ifname[IF_NAMESIZE];
+	int index;
+
+	while (len > 0) {
+		const struct nlmsghdr *hdr = buf;
+
+		if (!NLMSG_OK(hdr, len))
+			break;
+
+		switch (hdr->nlmsg_type) {
+		case RTM_NEWLINK:
+			req = g_slist_nth_data(pending_requests, 0);
+			if (req == NULL)
+				break;
+
+			msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+			parse_newlink_param(msg, IFA_PAYLOAD(hdr),
+							&index, ifname);
+
+			if (req->callback)
+				req->callback(index, ifname, req->user_data);
+			break;
+
+		case NLMSG_ERROR:
+			req = find_request(hdr->nlmsg_seq);
+			if (req == NULL)
+				break;
+
+			DBG("nlmsg error req");
+
+			if (req->callback)
+				req->callback(-1, ifname, req->user_data);
+			break;
+
+		default:
+			req = NULL;
+			break;
+		}
+
+		len -= hdr->nlmsg_len;
+		buf += hdr->nlmsg_len;
+
+		if (req) {
+			pending_requests = g_slist_remove(pending_requests,
+								req);
+			g_free(req);
+		}
+	}
+}
+
+static int add_attribute(struct nlmsghdr *n, unsigned int maxlen, int type,
+				const void *data, int datalen)
+{
+	int len = RTA_LENGTH(datalen);
+	struct rtattr *rta;
+
+	if ((NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) {
+		DBG("attribute to large for message %d %d %d\n",
+				n->nlmsg_len, len, maxlen);
+		return -1;
+	}
+
+	rta = NLMSG_TAIL(n);
+	rta->rta_type = type;
+	rta->rta_len = len;
+	memcpy(RTA_DATA(rta), data, datalen);
+	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+
+	return 0;
+}
+
+static inline void prep_rtnl_req(struct rtnl_msg *msg, int reqtype, __u32 seqnr)
+{
+	msg->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	msg->n.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL;
+	msg->n.nlmsg_type = reqtype;
+	msg->n.nlmsg_seq = seqnr;
+	msg->i.ifi_family = AF_UNSPEC;
+}
+
+static gboolean netlink_event(GIOChannel *chan,
+				GIOCondition cond, void *data)
+{
+	unsigned char buf[RTNL_MSG_SIZE];
+	int len, sk;
+
+	if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+		rtnl_watch = 0;
+		return FALSE;
+	}
+
+	sk = g_io_channel_unix_get_fd(rtnl_channel);
+
+	len = recv(sk, buf, sizeof(buf), MSG_DONTWAIT);
+	if (len < 0) {
+		if (len == -EAGAIN)
+			return TRUE;
+
+		rtnl_watch = 0;
+		return FALSE;
+	}
+
+	parse_rtnl_message(buf, len);
+
+	return TRUE;
+}
+
+int caif_rtnl_init(void)
+{
+	struct sockaddr_nl addr;
+	int sk, err;
+
+	sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+	if (sk < 0)
+		return sk;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.nl_family = AF_NETLINK;
+	addr.nl_groups = RTMGRP_LINK;
+
+	err = bind(sk, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0) {
+		close(sk);
+		return err;
+	}
+
+	rtnl_channel = g_io_channel_unix_new(sk);
+	g_io_channel_set_flags(rtnl_channel, G_IO_FLAG_NONBLOCK, NULL);
+	g_io_channel_set_close_on_unref(rtnl_channel, TRUE);
+
+	rtnl_watch = g_io_add_watch(rtnl_channel,
+				G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+				netlink_event, NULL);
+
+	return 0;
+}
+
+void caif_rtnl_exit(void)
+{
+	GSList *list;
+
+	if (rtnl_watch > 0)
+		g_source_remove(rtnl_watch);
+
+	g_io_channel_unref(rtnl_channel);
+
+	for (list = pending_requests; list; list = list->next) {
+		struct iplink_req *req = list->data;
+		g_free(req);
+	}
+
+	g_slist_free(pending_requests);
+}
+
+int caif_rtnl_create_interface(int type, int connid, int loop,
+				caif_rtnl_create_cb_t cb, void *user_data)
+{
+	struct iplink_req *req;
+	struct sockaddr_nl addr;
+	struct rtnl_msg msg;
+	struct rtattr *linkinfo;
+	struct rtattr *data_start;
+	int err, sk;
+
+	req = g_try_new0(struct iplink_req, 1);
+	if (req == NULL)
+		return -ENOMEM;
+
+	req->user_data = user_data;
+	req->callback = cb;
+	memset(&msg, 0, RTNL_MSG_SIZE);
+
+	req->rtnlmsg_seqnr = ++rtnl_seqnr;
+	prep_rtnl_req(&msg, RTM_NEWLINK, req->rtnlmsg_seqnr);
+
+	linkinfo = NLMSG_TAIL(&msg.n);
+	add_attribute(&msg.n, sizeof(msg), IFLA_LINKINFO,
+			NULL, 0);
+	add_attribute(&msg.n, sizeof(msg), IFLA_INFO_KIND,
+			"caif", 4);
+	data_start = NLMSG_TAIL(&msg.n);
+	add_attribute(&msg.n, sizeof(msg), IFLA_INFO_DATA,
+			NULL, 0);
+
+	switch (type) {
+	case IFLA_CAIF_IPV4_CONNID:
+	case IFLA_CAIF_IPV6_CONNID:
+		add_attribute(&msg.n, sizeof(msg),
+				type, &connid,
+				sizeof(connid));
+		break;
+	default:
+		DBG("unsupported linktype");
+		g_free(req);
+		return -EINVAL;
+	}
+
+	if (loop)
+		add_attribute(&msg.n, sizeof(msg),
+				IFLA_CAIF_LOOPBACK, &loop, sizeof(loop));
+
+	data_start->rta_len = (void *)NLMSG_TAIL(&msg.n) - (void *)data_start;
+	linkinfo->rta_len = (void *)NLMSG_TAIL(&msg.n) - (void *)linkinfo;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.nl_family = AF_NETLINK;
+
+	sk = g_io_channel_unix_get_fd(rtnl_channel);
+
+	err = sendto(sk, &msg, msg.n.nlmsg_len, 0,
+			(struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0) {
+		g_free(req);
+		return err;
+	}
+
+	pending_requests = g_slist_append(pending_requests, req);
+
+	return 0;
+}
+
+int caif_rtnl_delete_interface(int index)
+{
+	struct sockaddr_nl addr;
+	struct rtnl_msg msg;
+	int err, sk;
+
+	if (index < 0)
+		return -EINVAL;
+
+	sk = g_io_channel_unix_get_fd(rtnl_channel);
+
+	memset(&addr, 0, sizeof(addr));
+	addr.nl_family = AF_NETLINK;
+
+	memset(&msg, 0, sizeof(msg));
+	prep_rtnl_req(&msg, RTM_DELLINK, ++rtnl_seqnr);
+	msg.i.ifi_index = index;
+
+	err = sendto(sk, &msg, msg.n.nlmsg_len, 0,
+			(struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
--- drivers/stemodem/caif_rtnl.h
+++ drivers/stemodem/caif_rtnl.h
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010 ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef void (*caif_rtnl_create_cb_t) (int index, const char *ifname,
+							void *user_data);
+
+extern int caif_rtnl_create_interface(int type, int connid, int loop,
+				caif_rtnl_create_cb_t cb, void *user_data);
+extern int caif_rtnl_delete_interface(int index);
+
+extern int caif_rtnl_init(void);
+extern void caif_rtnl_exit(void);
--- drivers/stemodem/gprs-context.c
+++ drivers/stemodem/gprs-context.c
@@ -47,9 +47,9 @@
 #include "caif_socket.h"
 #include "if_caif.h"
 
-#define MAX_CAIF_DEVICES 7
+#define MAX_CAIF_DEVICES 4
 #define MAX_DNS 2
-#define MAX_ELEM 20
+#define IP_ADDR_LEN 20
 
 #define AUTH_BUF_LENGTH (OFONO_GPRS_MAX_USERNAME_LENGTH + \
 			OFONO_GPRS_MAX_PASSWORD_LENGTH + 128)
@@ -73,13 +73,13 @@
 
 struct eppsd_response {
 	char *current;
-	char ip_address[MAX_ELEM];
-	char subnet_mask[MAX_ELEM];
-	char mtu[MAX_ELEM];
-	char default_gateway[MAX_ELEM];
-	char dns_server1[MAX_ELEM];
-	char dns_server2[MAX_ELEM];
-	char p_cscf_server[MAX_ELEM];
+	char ip_address[IP_ADDR_LEN];
+	char subnet_mask[IP_ADDR_LEN];
+	char mtu[IP_ADDR_LEN];
+	char default_gateway[IP_ADDR_LEN];
+	char dns_server1[IP_ADDR_LEN];
+	char dns_server2[IP_ADDR_LEN];
+	char p_cscf_server[IP_ADDR_LEN];
 };
 
 static void start_element_handler(GMarkupParseContext *context,
@@ -99,7 +99,7 @@
 	else if (!strcmp(element_name, "default_gateway"))
 		rsp->current = rsp->default_gateway;
 	else if (!strcmp(element_name, "dns_server") &&
-		rsp->dns_server1[0] == '\0')
+					rsp->dns_server1[0] == '\0')
 		rsp->current = rsp->dns_server1;
 	else if (!strcmp(element_name, "dns_server"))
 		rsp->current = rsp->dns_server2;
@@ -122,8 +122,8 @@
 	struct eppsd_response *rsp = user_data;
 
 	if (rsp->current) {
-		strncpy(rsp->current, text, MAX_ELEM);
-		rsp->current[MAX_ELEM] = 0;
+		strncpy(rsp->current, text, IP_ADDR_LEN);
+		rsp->current[IP_ADDR_LEN] = 0;
 	}
 }
 
@@ -191,12 +191,16 @@
 	ofono_gprs_context_cb_t cb = cbd->cb;
 	struct ofono_gprs_context *gc = cbd->user;
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
-	struct ofono_error error;
 	struct conn_info *conn;
 	GSList *l;
 
-	if (!ok)
-		goto error;
+	if (!ok) {
+		struct ofono_error error;
+
+		decode_at_error(&error, g_at_result_final_response(result));
+		cb(&error, cbd->data);
+		return;
+	}
 
 	l = g_slist_find_custom(g_caif_devices,
 				GUINT_TO_POINTER(gcd->active_context),
@@ -217,9 +221,6 @@
 	}
 
 	conn->cid = 0;
-
-	decode_at_error(&error, g_at_result_final_response(result));
-	cb(&error, cbd->data);
 	return;
 
 error:
@@ -237,7 +238,7 @@
 	GSList *l;
 	int i;
 	gsize length;
-	char *res_string;
+	const char *res_string;
 	const char *dns[MAX_DNS + 1];
 	struct eppsd_response rsp;
 	GMarkupParseContext *context = NULL;
@@ -255,8 +256,15 @@
 
 	conn = l->data;
 
-	if (!ok)
-		goto error;
+	if (!ok) {
+		struct ofono_error error;
+
+		conn->cid = 0;
+		gcd->active_context = 0;
+		decode_at_error(&error, g_at_result_final_response(result));
+		cb(&error, NULL, 0, NULL, NULL, NULL, NULL, cbd->data);
+		return;
+	}
 
 	rsp.current = NULL;
 	context = g_markup_parse_context_new(&parser, 0, &rsp, NULL);
@@ -266,7 +274,7 @@
 
 	for (i = 0; i < g_at_result_num_response_lines(result); i++) {
 		g_at_result_iter_next(&iter, NULL);
-		res_string = strdup(g_at_result_iter_raw_line(&iter));
+		res_string = g_at_result_iter_raw_line(&iter);
 		length = strlen(res_string);
 
 		if (!g_markup_parse_context_parse(context, res_string,
@@ -326,7 +334,6 @@
 		struct ofono_error error;
 
 		gcd->active_context = 0;
-
 		decode_at_error(&error, g_at_result_final_response(result));
 		cb(&error, NULL, 0, NULL, NULL, NULL, NULL, cbd->data);
 		return;
@@ -389,7 +396,7 @@
 	 * Set username and password, this should be done after CGDCONT
 	 * or an error can occur.  We don't bother with error checking
 	 * here
-	 * */
+	 */
 	snprintf(buf, sizeof(buf), "AT*EIAAUW=%d,1,\"%s\",\"%s\"",
 			ctx->cid, ctx->username, ctx->password);
 
@@ -398,6 +405,7 @@
 	return;
 
 error:
+	gcd->active_context = 0;
 	g_free(cbd);
 
 	CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL, NULL, NULL, data);
--- gatchat/gathdlc.c
+++ gatchat/gathdlc.c
@@ -51,7 +51,6 @@
 struct _GAtHDLC {
 	gint ref_count;
 	GAtIO *io;
-	guint write_watch;
 	struct ring_buffer *write_buffer;
 	unsigned char *decode_buffer;
 	guint decode_offset;
--- gatchat/gatio.c
+++ gatchat/gatio.c
@@ -315,6 +315,9 @@
 	if (io->read_watch > 0)
 		g_source_remove(io->read_watch);
 
+	if (io->write_watch > 0)
+		g_source_remove(io->write_watch);
+
 	return TRUE;
 }
 
--- gatchat/gatrawip.c
+++ gatchat/gatrawip.c
@@ -23,14 +23,25 @@
 #include <config.h>
 #endif
 
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+
 #include <glib.h>
 
+#include "ringbuffer.h"
 #include "gatrawip.h"
 
 struct _GAtRawIP {
 	gint ref_count;
 	GAtIO *io;
+	GAtIO *tun_io;
 	char *ifname;
+	struct ring_buffer *write_buffer;
+	struct ring_buffer *tun_write_buffer;
 	GAtDebugFunc debugf;
 	gpointer debug_data;
 };
@@ -61,6 +72,9 @@
 
 	rawip->ref_count = 1;
 
+	rawip->write_buffer = NULL;
+	rawip->tun_write_buffer = NULL;
+
 	rawip->io = g_at_io_ref(io);
 
 	return rawip;
@@ -84,19 +98,130 @@
 	if (g_atomic_int_dec_and_test(&rawip->ref_count) == FALSE)
 		return;
 
+	g_at_rawip_shutdown(rawip);
+
 	g_at_io_unref(rawip->io);
 	rawip->io = NULL;
 
 	g_free(rawip->ifname);
+	rawip->ifname = NULL;
+
 	g_free(rawip);
 }
 
+static gboolean can_write_data(gpointer data)
+{
+	GAtRawIP *rawip = data;
+	unsigned int len;
+	unsigned char *buf;
+	gsize bytes_written;
+
+	if (rawip->write_buffer == NULL)
+		return FALSE;
+
+	len = ring_buffer_len_no_wrap(rawip->write_buffer);
+	buf = ring_buffer_read_ptr(rawip->write_buffer, 0);
+
+	bytes_written = g_at_io_write(rawip->io, (gchar *) buf, len);
+	ring_buffer_drain(rawip->write_buffer, bytes_written);
+
+	if (ring_buffer_len(rawip->write_buffer) > 0)
+		return TRUE;
+
+	rawip->write_buffer = NULL;
+
+	return FALSE;
+}
+
+static gboolean tun_write_data(gpointer data)
+{
+	GAtRawIP *rawip = data;
+	unsigned int len;
+	unsigned char *buf;
+	gsize bytes_written;
+
+	if (rawip->tun_write_buffer == NULL)
+		return FALSE;
+
+	len = ring_buffer_len_no_wrap(rawip->tun_write_buffer);
+	buf = ring_buffer_read_ptr(rawip->tun_write_buffer, 0);
+
+	bytes_written = g_at_io_write(rawip->tun_io, (gchar *) buf, len);
+	ring_buffer_drain(rawip->tun_write_buffer, bytes_written);
+
+	if (ring_buffer_len(rawip->tun_write_buffer) > 0)
+		return TRUE;
+
+	rawip->tun_write_buffer = NULL;
+
+	return FALSE;
+}
+
+static void new_bytes(struct ring_buffer *rbuf, gpointer user_data)
+{
+	GAtRawIP *rawip = user_data;
+
+	rawip->tun_write_buffer = rbuf;
+
+	g_at_io_set_write_handler(rawip->tun_io, tun_write_data, rawip);
+}
+
+static void tun_bytes(struct ring_buffer *rbuf, gpointer user_data)
+{
+	GAtRawIP *rawip = user_data;
+
+	rawip->write_buffer = rbuf;
+
+	g_at_io_set_write_handler(rawip->io, can_write_data, rawip);
+}
+
+static void create_tun(GAtRawIP *rawip)
+{
+	GIOChannel *channel;
+	struct ifreq ifr;
+	int fd, err;
+
+	fd = open("/dev/net/tun", O_RDWR);
+	if (fd < 0)
+		return;
+
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+	strcpy(ifr.ifr_name, "gprs%d");
+
+	err = ioctl(fd, TUNSETIFF, (void *) &ifr);
+	if (err < 0) {
+		close(fd);
+		return;
+	}
+
+	rawip->ifname = g_strdup(ifr.ifr_name);
+
+	channel = g_io_channel_unix_new(fd);
+	if (channel == NULL) {
+		close(fd);
+		return;
+	}
+
+	rawip->tun_io = g_at_io_new(channel);
+
+	g_io_channel_set_buffered(channel, FALSE);
+
+	g_io_channel_unref(channel);
+}
+
 void g_at_rawip_open(GAtRawIP *rawip)
 {
 	if (rawip == NULL)
 		return;
 
-	/* open TUN/TAP device */
+	create_tun(rawip);
+
+	if (rawip->tun_io == NULL)
+		return;
+
+	g_at_io_set_read_handler(rawip->io, new_bytes, rawip);
+	g_at_io_set_read_handler(rawip->tun_io, tun_bytes, rawip);
 }
 
 void g_at_rawip_shutdown(GAtRawIP *rawip)
@@ -104,7 +229,17 @@
 	if (rawip == NULL)
 		return;
 
-	/* close TUN/TAP device */
+	if (rawip->tun_io == NULL)
+		return;
+
+	g_at_io_set_read_handler(rawip->io, NULL, NULL);
+	g_at_io_set_read_handler(rawip->tun_io, NULL, NULL);
+
+	rawip->write_buffer = NULL;
+	rawip->tun_write_buffer = NULL;
+
+	g_at_io_unref(rawip->tun_io);
+	rawip->tun_io = NULL;
 }
 
 const char *g_at_rawip_get_interface(GAtRawIP *rawip)
--- gisi/client.c
+++ gisi/client.c
@@ -744,16 +744,19 @@
  */
 void g_isi_remove_subscription(GIsiClient *client, uint8_t res, uint8_t type)
 {
+	void *ret;
 	GIsiIndication *ind;
 	unsigned int id = (res << 8) | type;
 
 	if (!client)
 		return;
 
-	ind = tdelete(&id, &client->inds.subs, g_isi_cmp);
-	if (!ind)
+	ret = tfind(&id, &client->inds.subs, g_isi_cmp);
+	if (!ret)
 		return;
 
+	ind = *(GIsiIndication **)ret;
+	tdelete(ind, &client->inds.subs, g_isi_cmp);
 	client->inds.count--;
 	g_free(ind);
 }
--- gisi/pep.c
+++ gisi/pep.c
@@ -148,6 +148,8 @@
 
 char *g_isi_pep_get_ifname(const GIsiPEP *pep, char *ifname)
 {
-	unsigned ifi = g_isi_pep_get_ifindex(pep);
-	return if_indextoname(ifi, ifname);
+	if (pep->gprs_fd == -1)
+		return NULL;
+
+	return if_indextoname(g_isi_pep_get_ifindex(pep), ifname);
 }
--- include/gprs-context.h
+++ include/gprs-context.h
@@ -39,6 +39,14 @@
 	OFONO_GPRS_PROTO_IPV6,
 };
 
+enum ofono_gprs_context_type {
+	OFONO_GPRS_CONTEXT_TYPE_ANY = 0,
+	OFONO_GPRS_CONTEXT_TYPE_INTERNET,
+	OFONO_GPRS_CONTEXT_TYPE_MMS,
+	OFONO_GPRS_CONTEXT_TYPE_WAP,
+	OFONO_GPRS_CONTEXT_TYPE_IMS,
+};
+
 struct ofono_gprs_primary_context {
 	unsigned int cid;
 	int direction;
@@ -84,6 +92,9 @@
 
 struct ofono_modem *ofono_gprs_context_get_modem(struct ofono_gprs_context *gc);
 
+void ofono_gprs_context_set_type(struct ofono_gprs_context *gc,
+					enum ofono_gprs_context_type type);
+
 #ifdef __cplusplus
 }
 #endif
--- include/modem.h
+++ include/modem.h
@@ -42,6 +42,8 @@
 
 struct ofono_modem *ofono_modem_create(const char *name, const char *type);
 int ofono_modem_register(struct ofono_modem *modem);
+
+ofono_bool_t ofono_modem_is_registered(struct ofono_modem *modem);
 void ofono_modem_remove(struct ofono_modem *modem);
 
 void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered);
--- include/radio-settings.h
+++ include/radio-settings.h
@@ -43,6 +43,12 @@
 						enum ofono_radio_access_mode mode,
 						void *data);
 
+typedef void (*ofono_radio_settings_fast_dormancy_set_cb_t)(const struct ofono_error *error,
+							void *data);
+typedef void (*ofono_radio_settings_fast_dormancy_query_cb_t)(const struct ofono_error *error,
+							ofono_bool_t enable,
+							void *data);
+
 struct ofono_radio_settings_driver {
 	const char *name;
 	int (*probe)(struct ofono_radio_settings *rs, unsigned int vendor,
@@ -55,6 +61,13 @@
 				enum ofono_radio_access_mode mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data);
+	void (*query_fast_dormancy)(struct ofono_radio_settings *rs,
+			ofono_radio_settings_fast_dormancy_query_cb_t cb,
+			void *data);
+	void (*set_fast_dormancy)(struct ofono_radio_settings *rs,
+				int enable,
+				ofono_radio_settings_fast_dormancy_set_cb_t,
+				void *data);
 };
 
 int ofono_radio_settings_driver_register(const struct ofono_radio_settings_driver *d);
--- include/stk.h
+++ include/stk.h
@@ -70,9 +70,6 @@
 void ofono_stk_proactive_command_handled_notify(struct ofono_stk *stk,
 						int length,
 						const unsigned char *pdu);
-void ofono_stk_terminal_response_sent_notify(struct ofono_stk *stk,
-						int length,
-						const unsigned char *pdu);
 
 #ifdef __cplusplus
 }
--- plugins/atgen.c
+++ plugins/atgen.c
-/*
- *
- *  oFono - Open Source Telephony
- *
- *  Copyright (C) 2008-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <stdlib.h>
-
-#include <glib.h>
-#include <gatchat.h>
-#include <gattty.h>
-
-#define OFONO_API_SUBJECT_TO_CHANGE
-#include <ofono/plugin.h>
-#include <ofono/log.h>
-#include <ofono/modem.h>
-#include <ofono/call-barring.h>
-#include <ofono/call-forwarding.h>
-#include <ofono/call-meter.h>
-#include <ofono/call-settings.h>
-#include <ofono/devinfo.h>
-#include <ofono/message-waiting.h>
-#include <ofono/netreg.h>
-#include <ofono/phonebook.h>
-#include <ofono/sim.h>
-#include <ofono/stk.h>
-#include <ofono/sms.h>
-#include <ofono/ssn.h>
-#include <ofono/ussd.h>
-#include <ofono/voicecall.h>
-
-#include <drivers/atmodem/atutil.h>
-#include <drivers/atmodem/sim-poll.h>
-
-#include <ofono/gprs.h>
-#include <ofono/gprs-context.h>
-
-static const char *tty_opts[] = {
-	"Baud",
-	"Read",
-	"Local",
-	"StopBits",
-	"DataBits",
-	"Parity",
-	"XonXoff",
-	"RtsCts",
-	NULL,
-};
-
-static int atgen_probe(struct ofono_modem *modem)
-{
-	return 0;
-}
-
-static void atgen_remove(struct ofono_modem *modem)
-{
-}
-
-static void atgen_debug(const char *str, void *user_data)
-{
-	const char *prefix = user_data;
-
-	ofono_info("%s%s", prefix, str);
-}
-
-static int atgen_enable(struct ofono_modem *modem)
-{
-	GAtChat *chat;
-	GIOChannel *channel;
-	GAtSyntax *syntax;
-	const char *device;
-	const char *value;
-	GHashTable *options;
-	int i;
-
-	DBG("%p", modem);
-
-	device = ofono_modem_get_string(modem, "Device");
-	if (!device)
-		return -EINVAL;
-
-	options = g_hash_table_new_full(g_str_hash, g_str_equal,
-					g_free, g_free);
-	if (!options)
-		return -ENOMEM;
-
-	for (i = 0; tty_opts[i]; i++) {
-		value = ofono_modem_get_string(modem, tty_opts[i]);
-
-		if (value == NULL)
-			continue;
-
-		g_hash_table_insert(options, g_strdup(tty_opts[i]),
-					g_strdup(value));
-	}
-
-	channel = g_at_tty_open(device, options);
-
-	g_hash_table_destroy(options);
-
-	if (!channel) {
-		return -EIO;
-	}
-
-	value = ofono_modem_get_string(modem, "GsmSyntax");
-	if (value) {
-		if (g_str_equal(value, "V1"))
-			syntax = g_at_syntax_new_gsmv1();
-		else if (g_str_equal(value, "Permissive"))
-			syntax = g_at_syntax_new_gsm_permissive();
-		else
-			return -EINVAL;
-	} else {
-		syntax = g_at_syntax_new_gsmv1();
-	}
-
-	chat = g_at_chat_new(channel, syntax);
-	g_at_syntax_unref(syntax);
-	g_io_channel_unref(channel);
-
-	if (!chat)
-		return -ENOMEM;
-
-	if (getenv("OFONO_AT_DEBUG"))
-		g_at_chat_set_debug(chat, atgen_debug, "");
-
-	ofono_modem_set_data(modem, chat);
-
-	return 0;
-}
-
-static int atgen_disable(struct ofono_modem *modem)
-{
-	GAtChat *chat = ofono_modem_get_data(modem);
-
-	DBG("%p", modem);
-
-	ofono_modem_set_data(modem, NULL);
-
-	g_at_chat_send(chat, "AT+CFUN=4", NULL, NULL, NULL, NULL);
-
-	g_at_chat_unref(chat);
-
-	return 0;
-}
-
-static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
-{
-	struct cb_data *cbd = user_data;
-	ofono_modem_online_cb_t cb = cbd->cb;
-
-	if (ok)
-		CALLBACK_WITH_SUCCESS(cb, cbd->data);
-	else
-		CALLBACK_WITH_FAILURE(cb, cbd->data);
-}
-
-static void atgen_set_online(struct ofono_modem *modem, ofono_bool_t online,
-				ofono_modem_online_cb_t cb, void *user_data)
-{
-	GAtChat *chat = ofono_modem_get_data(modem);
-	struct cb_data *cbd = cb_data_new(cb, user_data);
-	char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
-
-	DBG("modem %p %s", modem, online ? "online" : "offline");
-
-	if (!cbd)
-		goto error;
-
-	if (g_at_chat_send(chat, command, NULL, set_online_cb, cbd, g_free))
-		return;
-
-error:
-	g_free(cbd);
-
-	CALLBACK_WITH_FAILURE(cb, cbd->data);
-}
-
-static void atgen_pre_sim(struct ofono_modem *modem)
-{
-	GAtChat *chat = ofono_modem_get_data(modem);
-	struct ofono_sim *sim;
-
-	DBG("%p", modem);
-
-	ofono_devinfo_create(modem, 0, "atmodem", chat);
-	sim = ofono_sim_create(modem, 0, "atmodem", chat);
-	ofono_voicecall_create(modem, 0, "atmodem", chat);
-
-	if (sim)
-		ofono_sim_inserted_notify(sim, TRUE);
-}
-
-static void atgen_post_sim(struct ofono_modem *modem)
-{
-	GAtChat *chat = ofono_modem_get_data(modem);
-
-	DBG("%p", modem);
-
-	ofono_phonebook_create(modem, 0, "atmodem", chat);
-}
-
-static void atgen_post_online(struct ofono_modem *modem)
-{
-	GAtChat *chat = ofono_modem_get_data(modem);
-	struct ofono_message_waiting *mw;
-	struct ofono_gprs *gprs;
-	struct ofono_gprs_context *gc;
-
-	DBG("%p", modem);
-
-	ofono_ussd_create(modem, 0, "atmodem", chat);
-	ofono_call_forwarding_create(modem, 0, "atmodem", chat);
-	ofono_call_settings_create(modem, 0, "atmodem", chat);
-	ofono_netreg_create(modem, 0, "atmodem", chat);
-	ofono_call_meter_create(modem, 0, "atmodem", chat);
-	ofono_call_barring_create(modem, 0, "atmodem", chat);
-	ofono_ssn_create(modem, 0, "atmodem", chat);
-	ofono_sms_create(modem, 0, "atmodem", chat);
-	gprs = ofono_gprs_create(modem,0, "atmodem", chat);
-	gc = ofono_gprs_context_create(modem, 0, "atmodem", chat);
-	if (gprs && gc)
-		ofono_gprs_add_context(gprs, gc);
-
-	mw = ofono_message_waiting_create(modem);
-	if (mw)
-		ofono_message_waiting_register(mw);
-}
-
-static struct ofono_modem_driver atgen_driver = {
-	.name		= "atgen",
-	.probe		= atgen_probe,
-	.remove		= atgen_remove,
-	.enable		= atgen_enable,
-	.disable	= atgen_disable,
-	.set_online     = atgen_set_online,
-	.pre_sim	= atgen_pre_sim,
-	.post_sim	= atgen_post_sim,
-	.post_online	= atgen_post_online,
-};
-
-static int atgen_init(void)
-{
-	return ofono_modem_driver_register(&atgen_driver);
-}
-
-static void atgen_exit(void)
-{
-	ofono_modem_driver_unregister(&atgen_driver);
-}
-
-OFONO_PLUGIN_DEFINE(atgen, "Generic AT driver", VERSION,
-		OFONO_PLUGIN_PRIORITY_DEFAULT, atgen_init, atgen_exit)
--- plugins/ifx.c
+++ plugins/ifx.c
@@ -62,19 +62,21 @@
 #include <drivers/atmodem/atutil.h>
 #include <drivers/atmodem/vendor.h>
 
-#define NUM_DLC  5
+#define NUM_DLC  6
 
 #define VOICE_DLC   0
 #define NETREG_DLC  1
 #define GPRS1_DLC   2
 #define GPRS2_DLC   3
-#define AUX_DLC     4
+#define GPRS3_DLC   4
+#define AUX_DLC     5
 
-static char *dlc_prefixes[NUM_DLC] = { "Voice: ", "Net: ",
-					"GPRS1: ", "GPRS2: ", "Aux: " };
+static char *dlc_prefixes[NUM_DLC] = { "Voice: ", "Net: ", "GPRS1: ",
+					"GPRS2: ", "GPRS3: ", "Aux: " };
 
 static const char *dlc_nodes[NUM_DLC] = { "/dev/ttyGSM1", "/dev/ttyGSM2",
-			"/dev/ttyGSM3", "/dev/ttyGSM4", "/dev/ttyGSM6" };
+					"/dev/ttyGSM3", "/dev/ttyGSM4",
+					"/dev/ttyGSM5", "/dev/ttyGSM6" };
 
 static const char *none_prefix[] = { NULL };
 static const char *xdrv_prefix[] = { "+XDRV:", NULL };
@@ -86,6 +88,7 @@
 	guint dlc_poll_count;
 	guint dlc_poll_source;
 	guint dlc_init_source;
+	guint mux_init_timeout;
 	guint frame_size;
 	int mux_ldisc;
 	int saved_ldisc;
@@ -182,28 +185,6 @@
 	}
 }
 
-static GAtChat *create_chat(GIOChannel *channel, char *debug)
-{
-	GAtSyntax *syntax;
-	GAtChat *chat;
-
-	if (!channel)
-		return NULL;
-
-	syntax = g_at_syntax_new_gsmv1();
-	chat = g_at_chat_new(channel, syntax);
-	g_at_syntax_unref(syntax);
-	g_io_channel_unref(channel);
-
-	if (!chat)
-		return NULL;
-
-	if (getenv("OFONO_AT_DEBUG"))
-		g_at_chat_set_debug(chat, ifx_debug, debug);
-
-	return chat;
-}
-
 static void shutdown_device(struct ifx_data *data)
 {
 	int i, fd;
@@ -240,6 +221,43 @@
 	data->device = NULL;
 }
 
+static void dlc_disconnect(gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct ifx_data *data = ofono_modem_get_data(modem);
+
+	DBG("");
+
+	ofono_warn("Disconnect of modem channel");
+
+	shutdown_device(data);
+}
+
+static GAtChat *create_chat(GIOChannel *channel, struct ofono_modem *modem,
+								char *debug)
+{
+	GAtSyntax *syntax;
+	GAtChat *chat;
+
+	if (!channel)
+		return NULL;
+
+	syntax = g_at_syntax_new_gsmv1();
+	chat = g_at_chat_new(channel, syntax);
+	g_at_syntax_unref(syntax);
+	g_io_channel_unref(channel);
+
+	if (!chat)
+		return NULL;
+
+	if (getenv("OFONO_AT_DEBUG"))
+		g_at_chat_set_debug(chat, ifx_debug, debug);
+
+	g_at_chat_set_disconnect_function(chat, dlc_disconnect, modem);
+
+	return chat;
+}
+
 static void xgendata_query(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct ofono_modem *modem = user_data;
@@ -357,6 +375,7 @@
 
 	g_at_chat_set_slave(data->dlcs[GPRS1_DLC], data->dlcs[NETREG_DLC]);
 	g_at_chat_set_slave(data->dlcs[GPRS2_DLC], data->dlcs[NETREG_DLC]);
+	g_at_chat_set_slave(data->dlcs[GPRS3_DLC], data->dlcs[NETREG_DLC]);
 
 	g_at_chat_send(data->dlcs[AUX_DLC], "AT+CFUN=4", NULL,
 					cfun_enable, modem, NULL);
@@ -388,7 +407,7 @@
 	for (i = 0; i < NUM_DLC; i++) {
 		GIOChannel *channel = g_at_tty_open(dlc_nodes[i], NULL);
 
-		data->dlcs[i] = create_chat(channel, dlc_prefixes[i]);
+		data->dlcs[i] = create_chat(channel, modem, dlc_prefixes[i]);
 		if (!data->dlcs[i]) {
 			ofono_error("Failed to open %s", dlc_nodes[i]);
 			goto error;
@@ -437,7 +456,7 @@
 	for (i = 0; i < NUM_DLC; i++) {
 		GIOChannel *channel = g_at_mux_create_channel(data->mux);
 
-		data->dlcs[i] = create_chat(channel, dlc_prefixes[i]);
+		data->dlcs[i] = create_chat(channel, modem, dlc_prefixes[i]);
 		if (!data->dlcs[i]) {
 			ofono_error("Failed to create channel");
 			goto error;
@@ -462,6 +481,11 @@
 
 	DBG("");
 
+	if (data->mux_init_timeout > 0) {
+		g_source_remove(data->mux_init_timeout);
+		data->mux_init_timeout = 0;
+	}
+
 	g_at_chat_unref(data->dlcs[AUX_DLC]);
 	data->dlcs[AUX_DLC] = NULL;
 
@@ -501,6 +525,26 @@
 	ofono_modem_set_powered(modem, FALSE);
 }
 
+static gboolean mux_timeout_cb(gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct ifx_data *data = ofono_modem_get_data(modem);
+
+	ofono_error("Timeout with multiplexer setup");
+
+	data->mux_init_timeout = 0;
+
+	g_at_chat_unref(data->dlcs[AUX_DLC]);
+	data->dlcs[AUX_DLC] = NULL;
+
+	g_io_channel_unref(data->device);
+	data->device = NULL;
+
+	ofono_modem_set_powered(modem, FALSE);
+
+	return FALSE;
+}
+
 static int ifx_enable(struct ofono_modem *modem)
 {
 	struct ifx_data *data = ofono_modem_get_data(modem);
@@ -559,6 +603,9 @@
 	g_at_chat_send(chat, "AT+CMUX=0,0,,1509,10,3,30,,", NULL,
 					mux_setup_cb, modem, NULL);
 
+	data->mux_init_timeout = g_timeout_add_seconds(5, mux_timeout_cb,
+								modem);
+
 	data->dlcs[AUX_DLC] = chat;
 
 	return -EINPROGRESS;
@@ -663,7 +710,7 @@
 	struct ifx_data *data = ofono_modem_get_data(modem);
 	struct ofono_message_waiting *mw;
 	struct ofono_gprs *gprs;
-	struct ofono_gprs_context *gc1, *gc2;
+	struct ofono_gprs_context *gc;
 
 	DBG("%p", modem);
 
@@ -687,26 +734,27 @@
 	if (mw)
 		ofono_message_waiting_register(mw);
 
-	gprs = ofono_gprs_create(modem, 0, "atmodem", data->dlcs[NETREG_DLC]);
+	gprs = ofono_gprs_create(modem, OFONO_VENDOR_IFX,
+					"atmodem", data->dlcs[NETREG_DLC]);
 	if (!gprs)
 		return;
 
 	if (data->mux_ldisc < 0) {
-		gc1 = ofono_gprs_context_create(modem, 0,
-					"atmodem", data->dlcs[GPRS1_DLC]);
-		gc2 = ofono_gprs_context_create(modem, 0,
-					"atmodem", data->dlcs[GPRS2_DLC]);
-	} else {
-		gc1 = ofono_gprs_context_create(modem, 0,
+		gc = ofono_gprs_context_create(modem, 0,
 					"ifxmodem", data->dlcs[GPRS1_DLC]);
-		gc2 = ofono_gprs_context_create(modem, 0,
+		if (gc)
+			ofono_gprs_add_context(gprs, gc);
+
+		gc = ofono_gprs_context_create(modem, 0,
 					"ifxmodem", data->dlcs[GPRS2_DLC]);
-	}
+		if (gc)
+			ofono_gprs_add_context(gprs, gc);
 
-	if (gc1)
-		ofono_gprs_add_context(gprs, gc1);
-	if (gc2)
-		ofono_gprs_add_context(gprs, gc2);
+		gc = ofono_gprs_context_create(modem, 0,
+					"ifxmodem", data->dlcs[GPRS3_DLC]);
+		if (gc)
+			ofono_gprs_add_context(gprs, gc);
+	}
 }
 
 static struct ofono_modem_driver ifx_driver = {
--- plugins/isigen.c
+++ plugins/isigen.c
@@ -58,6 +58,8 @@
 #include "drivers/isimodem/mtc.h"
 #include "drivers/isimodem/debug.h"
 
+#define ISI_DEFAULT_PDPS 4	/* Number of supported PDP contexts */
+
 struct isi_data {
 	struct ofono_modem *modem;
 	char const *ifname;
@@ -286,9 +288,10 @@
 	}
 
 	if (address) {
-		int error = g_pn_netlink_set_address(idx, PN_DEV_PC);
+		int error = g_pn_netlink_set_address(idx, address);
 		if (error && error != -EEXIST) {
 			DBG("g_pn_netlink_set_address: %s\n", strerror(-error));
+			g_pn_netlink_stop(link);
 			return -errno;
 		}
 	}
@@ -372,7 +375,7 @@
 
 	isi->online = online;
 
-	if (g_isi_send(isi->client, req, sizeof(req), MTC_TIMEOUT,
+	if (g_isi_send(isi->client, req, sizeof(req), MTC_STATE_REQ_TIMEOUT,
 			mtc_state_cb, cbd, NULL))
 		return;
 
@@ -406,6 +409,7 @@
 	struct isi_data *isi = ofono_modem_get_data(modem);
 	struct ofono_gprs *gprs;
 	struct ofono_gprs_context *gc;
+	int i;
 
 	DBG("(%p) with %s", modem, isi->ifname);
 
@@ -419,13 +423,21 @@
 	ofono_call_barring_create(isi->modem, 0, "isimodem", isi->idx);
 	ofono_call_meter_create(isi->modem, 0, "isimodem", isi->idx);
 	ofono_radio_settings_create(isi->modem, 0, "isimodem", isi->idx);
+
 	gprs = ofono_gprs_create(isi->modem, 0, "isimodem", isi->idx);
-	gc = ofono_gprs_context_create(isi->modem, 0, "isimodem", isi->idx);
+	if (!gprs)
+		return;
+
+	for (i = 0; i < ISI_DEFAULT_PDPS; i++) {
+		gc = ofono_gprs_context_create(isi->modem, 0,
+						"isimodem", isi->idx);
+		if (!gc) {
+			DBG("Failed to add context %d", i);
+			break;
+		}
 
-	if (gprs && gc)
 		ofono_gprs_add_context(gprs, gc);
-	else
-		DBG("Failed to add context");
+	}
 }
 
 static int isigen_enable(struct ofono_modem *modem)
--- plugins/mbm.c
+++ plugins/mbm.c
@@ -64,6 +64,9 @@
 	guint cpin_poll_source;
 	guint cpin_poll_count;
 	gboolean have_sim;
+	struct ofono_gprs *gprs;
+	struct ofono_gprs_context *gc;
+	guint reopen_source;
 	enum mbm_variant variant;
 };
 
@@ -294,6 +297,58 @@
 	return chat;
 }
 
+static void mbm_disconnect(gpointer user_data);
+
+static gboolean reopen_callback(gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mbm_data *data = ofono_modem_get_data(modem);
+	const char *data_dev;
+
+	data->reopen_source = 0;
+
+	data_dev = ofono_modem_get_string(modem, "DataDevice");
+
+	data->data_port = create_port(data_dev);
+	if (data->data_port == NULL)
+		return FALSE;
+
+	if (getenv("OFONO_AT_DEBUG"))
+		g_at_chat_set_debug(data->data_port, mbm_debug, "Data: ");
+
+	g_at_chat_set_disconnect_function(data->data_port,
+						mbm_disconnect, modem);
+
+	ofono_info("Reopened GPRS context channel");
+
+	data->gc = ofono_gprs_context_create(modem, 0,
+					"atmodem", data->data_port);
+	if (data->gprs && data->gc) {
+		ofono_gprs_context_set_type(data->gc,
+					OFONO_GPRS_CONTEXT_TYPE_MMS);
+		ofono_gprs_add_context(data->gprs, data->gc);
+	}
+
+	return FALSE;
+}
+
+static void mbm_disconnect(gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mbm_data *data = ofono_modem_get_data(modem);
+
+	DBG("");
+
+	if (data->gc)
+		ofono_gprs_context_remove(data->gc);
+
+	g_at_chat_unref(data->data_port);
+	data->data_port = NULL;
+
+	/* Waiting for the +CGEV: ME DEACT might also work */
+	data->reopen_source = g_timeout_add_seconds(1, reopen_callback, modem);
+}
+
 static int mbm_enable(struct ofono_modem *modem)
 {
 	struct mbm_data *data = ofono_modem_get_data(modem);
@@ -311,7 +366,6 @@
 		return -EINVAL;
 
 	data->modem_port = create_port(modem_dev);
-
 	if (data->modem_port == NULL)
 		return -EIO;
 
@@ -319,7 +373,6 @@
 		g_at_chat_set_debug(data->modem_port, mbm_debug, "Modem: ");
 
 	data->data_port = create_port(data_dev);
-
 	if (data->data_port == NULL) {
 		g_at_chat_unref(data->modem_port);
 		data->modem_port = NULL;
@@ -330,11 +383,17 @@
 	if (getenv("OFONO_AT_DEBUG"))
 		g_at_chat_set_debug(data->data_port, mbm_debug, "Data: ");
 
+	g_at_chat_set_disconnect_function(data->data_port,
+						mbm_disconnect, modem);
+
 	g_at_chat_register(data->modem_port, "*EMRDY:", emrdy_notifier,
 					FALSE, modem, NULL);
 
 	g_at_chat_send(data->modem_port, "AT&F E0 V1 X4 &C1 +CMEE=1", NULL,
 					NULL, NULL, NULL);
+	g_at_chat_send(data->data_port, "AT&F E0 V1 X4 &C1 +CMEE=1", NULL,
+					NULL, NULL, NULL);
+
 	g_at_chat_send(data->modem_port, "AT*E2CFUN=1", none_prefix,
 					NULL, NULL, NULL);
 	g_at_chat_send(data->modem_port, "AT*EMRDY?", none_prefix,
@@ -366,6 +425,11 @@
 
 	DBG("%p", modem);
 
+	if (data->reopen_source > 0) {
+		g_source_remove(data->reopen_source);
+		data->reopen_source = 0;
+	}
+
 	if (!data->modem_port)
 		return 0;
 
@@ -437,7 +501,6 @@
 static void mbm_post_online(struct ofono_modem *modem)
 {
 	struct mbm_data *data = ofono_modem_get_data(modem);
-	struct ofono_gprs *gprs;
 	struct ofono_gprs_context *gc;
 
 	DBG("%p", modem);
@@ -448,22 +511,36 @@
 	ofono_sms_create(modem, 0, "atmodem", data->modem_port);
 
 	switch (data->variant) {
+	case MBM_GENERIC:
+		ofono_cbs_create(modem, 0, "atmodem", data->modem_port);
+		break;
 	case MBM_DELL_D5530:
 		/* DELL D5530 crashes when it processes CBSs */
 		break;
-	default:
-		ofono_cbs_create(modem, 0, "atmodem", data->modem_port);
 	}
 
 	ofono_ussd_create(modem, 0, "atmodem", data->modem_port);
 
-	gprs = ofono_gprs_create(modem, OFONO_VENDOR_MBM,
+	data->gprs = ofono_gprs_create(modem, OFONO_VENDOR_MBM,
 					"atmodem", data->modem_port);
+	if (!data->gprs)
+		return;
+
 	gc = ofono_gprs_context_create(modem, 0,
 					"mbmmodem", data->modem_port);
+	if (gc) {
+		ofono_gprs_context_set_type(gc,
+					OFONO_GPRS_CONTEXT_TYPE_INTERNET);
+		ofono_gprs_add_context(data->gprs, gc);
+	}
 
-	if (gprs && gc)
-		ofono_gprs_add_context(gprs, gc);
+	data->gc = ofono_gprs_context_create(modem, 0,
+					"atmodem", data->data_port);
+	if (data->gc) {
+		ofono_gprs_context_set_type(data->gc,
+					OFONO_GPRS_CONTEXT_TYPE_MMS);
+		ofono_gprs_add_context(data->gprs, data->gc);
+	}
 }
 
 static struct ofono_modem_driver mbm_driver = {
--- plugins/modem.conf
+++ plugins/modem.conf
-# This is a sample file for the static modem configuration
-#
-# It should be installed in your oFono system directory,
-# e.g. /etc/ofono/modem.conf
-#
-# Each group is parsed as a modem device
-# Each group shall at least define the driver
-# Driver = <driver string>, e.g. phonesim, atgen, g1, calypso etc.
-#
-# If driver is phonesim, the following keys are required:
-# Address = <valid IPv4 address format>
-# Port = <valid TCP port>
-#
-# If driver is atgen, g1 or calypso, the following key is required
-# Device = <device path>
-
-# Sample for using phone simulator
-#[phonesim]
-#Driver=phonesim
-#Address=127.0.0.1
-#Port=12345
-
-# Sample for using generic driver
-#[generic]
-#Driver=atgen
-#Device=/dev/ttyS0
-
-# Sample for Android/HTC G1
-#[g1]
-#Driver=g1
-#Device=/dev/smd0
-
-# Sample for Openmoko Freerunner
-#[freerunner]
-#Driver=calypso
-#Device=/dev/ttySAC0
-
-# Nokia N900 with Maemo daemons
-#[n900]
-#Driver=isimodem
-#Interface=phonet0
-
-# Nokia N900 without Maemo daemons
-#[n900]
-#Driver=n900modem
-#Interface=phonet0
-
-# Sample STE modem
-#[ste]
-#Interface=cfttyS0
-#Driver=ste
--- plugins/modemconf.c
+++ plugins/modemconf.c
-/*
- *
- *  oFono - Open Source Telephony
- *
- *  Copyright (C) 2008-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <stdlib.h>
-
-#include <glib.h>
-
-#define OFONO_API_SUBJECT_TO_CHANGE
-#include <ofono/plugin.h>
-#include <ofono/modem.h>
-#include <ofono/log.h>
-
-static GSList *modem_list = NULL;
-
-static const char *tty_opts[] = {
-	"Baud",
-	"Read",
-	"Local",
-	"StopBits",
-	"DataBits",
-	"Parity",
-	"XonXoff",
-	"RtsCts",
-	"GsmSyntax",
-	NULL,
-};
-
-static int set_address(struct ofono_modem *modem,
-					GKeyFile *keyfile, const char *group)
-{
-	char *value;
-
-	value = g_key_file_get_string(keyfile, group, "Address", NULL);
-	if (value) {
-		ofono_modem_set_string(modem, "Address", value);
-		g_free(value);
-	} else {
-		ofono_modem_set_string(modem, "Address", "127.0.0.1");
-	}
-
-	value = g_key_file_get_string(keyfile, group, "Port", NULL);
-	if (value) {
-		ofono_modem_set_integer(modem, "Port", atoi(value));
-		g_free(value);
-	} else {
-		ofono_modem_set_integer(modem, "Port", 12345);
-	}
-
-	value = g_key_file_get_string(keyfile, group, "Modem", NULL);
-	if (value) {
-		ofono_modem_set_string(modem, "Modem", value);
-		g_free(value);
-	}
-
-	value = g_key_file_get_string(keyfile, group, "Multiplexer", NULL);
-	if (value) {
-		ofono_modem_set_string(modem, "Multiplexer", value);
-		g_free(value);
-	}
-
-	return 0;
-}
-
-static int set_device(struct ofono_modem *modem,
-					GKeyFile *keyfile, const char *group)
-{
-	char *device;
-	char *value;
-	int i;
-
-	device = g_key_file_get_string(keyfile, group, "Device", NULL);
-	if (!device)
-		return -EINVAL;
-
-	ofono_modem_set_string(modem, "Device", device);
-
-	g_free(device);
-
-	for (i = 0; tty_opts[i]; i++) {
-		value = g_key_file_get_string(keyfile, group,
-						tty_opts[i], NULL);
-
-		if (value == NULL)
-			continue;
-
-		ofono_modem_set_string(modem, tty_opts[i], value);
-		g_free(value);
-	}
-
-	return 0;
-}
-
-static int set_interface(struct ofono_modem *modem,
-					GKeyFile *keyfile, const char *group)
-{
-	char *value;
-
-	value = g_key_file_get_string(keyfile, group, "Interface", NULL);
-	if (value)
-		ofono_modem_set_string(modem, "Interface", value);
-	g_free(value);
-
-	value = g_key_file_get_string(keyfile, group, "Address", NULL);
-	if (value)
-		ofono_modem_set_integer(modem, "Address", atoi(value));
-	g_free(value);
-
-	return 0;
-}
-
-static struct {
-	const char *driver;
-	int (*func) (struct ofono_modem *modem,
-				GKeyFile *keyfile, const char *group);
-} setup_helpers[] = {
-	{ "phonesim",	set_address	},
-	{ "atgen",	set_device	},
-	{ "g1",		set_device	},
-	{ "wavecom",	set_device	},
-	{ "ste",	set_device	},
-	{ "ste",	set_interface	},
-	{ "calypso",	set_device	},
-	{ "palmpre",	set_device	},
-	{ "isigen",     set_interface   },
-	{ "n900",	set_interface	},
-	{ NULL }
-};
-
-static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
-{
-	struct ofono_modem *modem;
-	char *driver;
-	int i;
-
-	driver = g_key_file_get_string(keyfile, group, "Driver", NULL);
-	if (!driver)
-		return NULL;
-
-	modem = ofono_modem_create(group, driver);
-	if (modem == NULL)
-		goto error;
-
-	for (i = 0; setup_helpers[i].driver; i++) {
-		if (!g_strcmp0(driver, setup_helpers[i].driver))
-			setup_helpers[i].func(modem, keyfile, group);
-	}
-
-error:
-	g_free(driver);
-
-	return modem;
-}
-
-static void parse_config(const char *file)
-{
-	GKeyFile *keyfile;
-	GError *err = NULL;
-	char **modems;
-	int i;
-
-	keyfile = g_key_file_new();
-
-	g_key_file_set_list_separator(keyfile, ',');
-
-	if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
-		ofono_warn("Reading of %s failed: %s", file, err->message);
-		g_error_free(err);
-		goto done;
-	}
-
-	modems = g_key_file_get_groups(keyfile, NULL);
-
-	for (i = 0; modems[i]; i++) {
-		struct ofono_modem *modem;
-
-		modem = create_modem(keyfile, modems[i]);
-		if (!modem)
-			continue;
-
-		modem_list = g_slist_prepend(modem_list, modem);
-
-		ofono_modem_register(modem);
-	}
-
-	g_strfreev(modems);
-
-done:
-	g_key_file_free(keyfile);
-}
-
-static int modemconf_init(void)
-{
-	parse_config(CONFIGDIR "/modem.conf");
-
-	return 0;
-}
-
-static void modemconf_exit(void)
-{
-	GSList *list;
-
-	for (list = modem_list; list; list = list->next) {
-		struct ofono_modem *modem = list->data;
-
-		ofono_modem_remove(modem);
-	}
-
-	g_slist_free(modem_list);
-	modem_list = NULL;
-}
-
-OFONO_PLUGIN_DEFINE(modemconf, "Static modem configuration", VERSION,
-		OFONO_PLUGIN_PRIORITY_DEFAULT, modemconf_init, modemconf_exit)
--- plugins/nokia.c
+++ plugins/nokia.c
@@ -130,7 +130,8 @@
 
 	DBG("");
 
-	ofono_gprs_context_remove(data->gc);
+	if (data->gc)
+		ofono_gprs_context_remove(data->gc);
 
 	g_at_chat_unref(data->modem);
 	data->modem = NULL;
--- plugins/ofono.rules
+++ plugins/ofono.rules
@@ -363,6 +363,8 @@
 # ZTE Incorporated
 ATTRS{idVendor}=="19d2", ENV{OFONO_DRIVER}="zte"
 
+ATTRS{idVendor}=="19d2", ATTRS{serial}=="1234567890ABCDEF", ENV{ID_SERIAL_SHORT}=""
+
 # Option Globetrotter
 ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6911", ENV{OFONO_DRIVER}="hso"
 ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6971", ENV{OFONO_DRIVER}="hso"
@@ -388,6 +390,9 @@
 ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190a", ENV{OFONO_DRIVER}="mbm"
 ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1909", ENV{OFONO_DRIVER}="mbm"
 
+# Ericsson F5521gw
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190d", ENV{OFONO_DRIVER}="mbm"
+
 # Sony-Ericsson MD300
 ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", ENV{OFONO_DRIVER}="mbm"
 
@@ -424,4 +429,7 @@
 # Nokia Internet Stick CS-10
 ATTRS{idVendor}=="0421", ATTRS{idProduct}=="060e", ENV{OFONO_DRIVER}="nokia"
 
+# Nokia Internet Stick CS-17
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0623", ENV{OFONO_DRIVER}="nokia"
+
 LABEL="ofono_end"
--- plugins/phonesim.c
+++ plugins/phonesim.c
@@ -64,6 +64,7 @@
 #include <drivers/atmodem/atutil.h>
 
 static const char *none_prefix[] = { NULL };
+static int next_iface = 0;
 
 struct phonesim_data {
 	GAtMux *mux;
@@ -72,6 +73,131 @@
 	gboolean use_mux;
 };
 
+struct gprs_context_data {
+	GAtChat *chat;
+	char *interface;
+};
+
+static void at_cgact_up_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_gprs_context_up_cb_t cb = cbd->cb;
+	struct ofono_gprs_context *gc = cbd->user;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct ofono_error error;
+
+	decode_at_error(&error, g_at_result_final_response(result));
+	cb(&error, ok ? gcd->interface : NULL, FALSE,
+			NULL, NULL, NULL, NULL, cbd->data);
+}
+
+static void at_cgact_down_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_gprs_context_cb_t cb = cbd->cb;
+	struct ofono_error error;
+
+	decode_at_error(&error, g_at_result_final_response(result));
+	cb(&error, cbd->data);
+}
+
+static void phonesim_activate_primary(struct ofono_gprs_context *gc,
+				const struct ofono_gprs_primary_context *ctx,
+				ofono_gprs_context_up_cb_t cb, void *data)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct cb_data *cbd = cb_data_new(cb, data);
+	char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
+	int len;
+
+	cbd->user = gc;
+
+	len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid);
+
+	if (ctx->apn)
+		snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"",
+				ctx->apn);
+
+	/* Assume always succeeds */
+	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+		goto error;
+
+	sprintf(buf, "AT+CGACT=1,%u", ctx->cid);
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				at_cgact_up_cb, cbd, g_free) > 0)
+		return;
+
+error:
+	g_free(cbd);
+
+	CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL, NULL, NULL, data);
+}
+
+static void phonesim_deactivate_primary(struct ofono_gprs_context *gc,
+					unsigned int id,
+					ofono_gprs_context_cb_t cb, void *data)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct cb_data *cbd = cb_data_new(cb, data);
+	char buf[128];
+
+	if (!cbd)
+		goto error;
+
+	cbd->user = gc;
+
+	snprintf(buf, sizeof(buf), "AT+CGACT=0,%u", id);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				at_cgact_down_cb, cbd, g_free) > 0)
+		return;
+
+error:
+	g_free(cbd);
+
+	CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static int phonesim_context_probe(struct ofono_gprs_context *gc,
+					unsigned int vendor, void *data)
+{
+	GAtChat *chat = data;
+	struct gprs_context_data *gcd;
+
+	gcd = g_try_new0(struct gprs_context_data, 1);
+	if (!gcd)
+		return -ENOMEM;
+
+	gcd->chat = g_at_chat_clone(chat);
+	gcd->interface = g_strdup_printf("dummy%d", next_iface++);
+
+	ofono_gprs_context_set_data(gc, gcd);
+
+	return 0;
+}
+
+static void phonesim_context_remove(struct ofono_gprs_context *gc)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+	DBG("");
+
+	ofono_gprs_context_set_data(gc, NULL);
+
+	g_at_chat_unref(gcd->chat);
+	g_free(gcd->interface);
+
+	g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver context_driver = {
+	.name			= "phonesim",
+	.probe			= phonesim_context_probe,
+	.remove			= phonesim_context_remove,
+	.activate_primary	= phonesim_activate_primary,
+	.deactivate_primary	= phonesim_deactivate_primary,
+};
+
 static int phonesim_probe(struct ofono_modem *modem)
 {
 	struct phonesim_data *data;
@@ -351,7 +477,7 @@
 	struct phonesim_data *data = ofono_modem_get_data(modem);
 	struct ofono_message_waiting *mw;
 	struct ofono_gprs *gprs;
-	struct ofono_gprs_context *gc;
+	struct ofono_gprs_context *gc1, *gc2;
 
 	DBG("%p", modem);
 
@@ -377,10 +503,14 @@
 	}
 
 	gprs = ofono_gprs_create(modem, 0, "atmodem", data->chat);
-	gc = ofono_gprs_context_create(modem, 0, "atmodem", data->chat);
 
-	if (gprs && gc)
-		ofono_gprs_add_context(gprs, gc);
+	gc1 = ofono_gprs_context_create(modem, 0, "phonesim", data->chat);
+	if (gprs && gc1)
+		ofono_gprs_add_context(gprs, gc1);
+
+	gc2 = ofono_gprs_context_create(modem, 0, "phonesim", data->chat);
+	if (gprs && gc2)
+		ofono_gprs_add_context(gprs, gc2);
 
 	mw = ofono_message_waiting_create(modem);
 	if (mw)
@@ -400,15 +530,128 @@
 	.post_online	= phonesim_post_online,
 };
 
+static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
+{
+	struct ofono_modem *modem;
+	char *value;
+
+	DBG("group %s", group);
+
+	modem = ofono_modem_create(group, "phonesim");
+	if (!modem)
+		return NULL;
+
+	value = g_key_file_get_string(keyfile, group, "Address", NULL);
+	if (!value)
+		goto error;
+
+	ofono_modem_set_string(modem, "Address", value);
+	g_free(value);
+
+	value = g_key_file_get_string(keyfile, group, "Port", NULL);
+	if (!value)
+		goto error;
+
+	ofono_modem_set_integer(modem, "Port", atoi(value));
+	g_free(value);
+
+	value = g_key_file_get_string(keyfile, group, "Modem", NULL);
+	if (value) {
+		ofono_modem_set_string(modem, "Modem", value);
+		g_free(value);
+	}
+
+	value = g_key_file_get_string(keyfile, group, "Multiplexer", NULL);
+	if (value) {
+		ofono_modem_set_string(modem, "Multiplexer", value);
+		g_free(value);
+	}
+
+	DBG("%p", modem);
+
+	return modem;
+
+error:
+	ofono_error("Missing address or port setting for %s", group);
+
+	ofono_modem_remove(modem);
+
+	return NULL;
+}
+
+static GSList *modem_list = NULL;
+
+static void parse_config(const char *filename)
+{
+	GKeyFile *keyfile;
+	GError *err = NULL;
+	char **modems;
+	int i;
+
+	DBG("filename %s", filename);
+
+	keyfile = g_key_file_new();
+
+	g_key_file_set_list_separator(keyfile, ',');
+
+	if (!g_key_file_load_from_file(keyfile, filename, 0, &err)) {
+		ofono_warn("Reading of %s failed: %s", filename, err->message);
+		g_error_free(err);
+		goto done;
+	}
+
+	modems = g_key_file_get_groups(keyfile, NULL);
+
+	for (i = 0; modems[i]; i++) {
+		struct ofono_modem *modem;
+
+		modem = create_modem(keyfile, modems[i]);
+		if (!modem)
+			continue;
+
+		modem_list = g_slist_prepend(modem_list, modem);
+
+		ofono_modem_register(modem);
+	}
+
+	g_strfreev(modems);
+
+done:
+	g_key_file_free(keyfile);
+}
+
 static int phonesim_init(void)
 {
-	return ofono_modem_driver_register(&phonesim_driver);
+	int err;
+
+	err = ofono_modem_driver_register(&phonesim_driver);
+	if (err < 0)
+		return err;
+
+	ofono_gprs_context_driver_register(&context_driver);
+
+	parse_config(CONFIGDIR "/phonesim.conf");
+
+	return 0;
 }
 
 static void phonesim_exit(void)
 {
+	GSList *list;
+
+	for (list = modem_list; list; list = list->next) {
+		struct ofono_modem *modem = list->data;
+
+		ofono_modem_remove(modem);
+	}
+
+	g_slist_free(modem_list);
+	modem_list = NULL;
+
+	ofono_gprs_context_driver_unregister(&context_driver);
+
 	ofono_modem_driver_unregister(&phonesim_driver);
 }
 
-OFONO_PLUGIN_DEFINE(phonesim, "PhoneSIM driver", VERSION,
+OFONO_PLUGIN_DEFINE(phonesim, "Phone Simulator driver", VERSION,
 		OFONO_PLUGIN_PRIORITY_DEFAULT, phonesim_init, phonesim_exit)
--- plugins/phonesim.conf
+++ plugins/phonesim.conf
+# This is a sample file for the phonesim configuration
+#
+# It should be installed in your oFono system directory,
+# e.g. /etc/ofono/phonesim.conf
+#
+# Each group is parsed as a modem device
+#
+# Each group shall at least define the address and port
+#   Address = <valid IPv4 address format>
+#   Port = <valid TCP port>
+
+#[phonesim]
+#Address=127.0.0.1
+#Port=12345
--- plugins/push-notification.c
+++ plugins/push-notification.c
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2010  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <glib.h>
+#include <gdbus.h>
+#include <ofono.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/dbus.h>
+
+#include "smsagent.h"
+
+#define PUSH_NOTIFICATION_INTERFACE "org.ofono.PushNotification"
+#define AGENT_INTERFACE "org.ofono.PushNotificationAgent"
+#define WAP_PUSH_SRC_PORT 9200
+#define WAP_PUSH_DST_PORT 2948
+
+static unsigned int modemwatch_id;
+
+struct push_notification {
+	struct ofono_modem *modem;
+	struct ofono_sms *sms;
+	struct sms_agent *agent;
+	unsigned int push_watch;
+};
+
+static void agent_exited(void *userdata)
+{
+	struct push_notification *pn = userdata;
+
+	if (pn->push_watch > 0) {
+		__ofono_sms_datagram_watch_remove(pn->sms, pn->push_watch);
+		pn->push_watch = 0;
+	}
+
+	pn->agent = NULL;
+}
+
+static void push_received(const char *from, const struct tm *remote,
+				const struct tm *local, int dst, int src,
+				const unsigned char *buffer,
+				unsigned int len, void *data)
+{
+	struct push_notification *pn = data;
+
+	DBG("Received push of size: %u", len);
+
+	if (pn->agent == NULL)
+		return;
+
+	sms_agent_dispatch_datagram(pn->agent, "ReceiveNotification",
+					from, remote, local, buffer, len,
+					NULL, NULL, NULL);
+}
+
+static DBusMessage *push_notification_register_agent(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct push_notification *pn = data;
+	const char *agent_path;
+
+	if (pn->agent)
+		return __ofono_error_busy(msg);
+
+	if (dbus_message_get_args(msg, NULL,
+					DBUS_TYPE_OBJECT_PATH, &agent_path,
+					DBUS_TYPE_INVALID) == FALSE)
+		return __ofono_error_invalid_args(msg);
+
+	if (!__ofono_dbus_valid_object_path(agent_path))
+		return __ofono_error_invalid_format(msg);
+
+	pn->agent = sms_agent_new(AGENT_INTERFACE,
+					dbus_message_get_sender(msg),
+					agent_path);
+
+	if (pn->agent == NULL)
+		return __ofono_error_failed(msg);
+
+	sms_agent_set_removed_notify(pn->agent, agent_exited, pn);
+
+	pn->push_watch = __ofono_sms_datagram_watch_add(pn->sms, push_received,
+							WAP_PUSH_DST_PORT,
+							WAP_PUSH_SRC_PORT,
+							pn, NULL);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *push_notification_unregister_agent(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct push_notification *pn = data;
+	const char *agent_path;
+	const char *agent_bus = dbus_message_get_sender(msg);
+
+	if (dbus_message_get_args(msg, NULL,
+					DBUS_TYPE_OBJECT_PATH, &agent_path,
+					DBUS_TYPE_INVALID) == FALSE)
+		return __ofono_error_invalid_args(msg);
+
+	if (pn->agent == NULL)
+		return __ofono_error_failed(msg);
+
+	if (sms_agent_matches(pn->agent, agent_bus, agent_path) == FALSE)
+		return __ofono_error_failed(msg);
+
+	sms_agent_free(pn->agent);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable push_notification_methods[] = {
+	{ "RegisterAgent",    "o",   "",  push_notification_register_agent },
+	{ "UnregisterAgent",  "o",   "",  push_notification_unregister_agent },
+	{ }
+};
+
+static void push_notification_cleanup(gpointer user)
+{
+	struct push_notification *pn = user;
+
+	DBG("%p", pn);
+
+	/* The push watch was already cleaned up */
+	pn->push_watch = 0;
+	pn->sms = NULL;
+
+	sms_agent_free(pn->agent);
+
+	ofono_modem_remove_interface(pn->modem, PUSH_NOTIFICATION_INTERFACE);
+}
+
+static void sms_watch(struct ofono_atom *atom,
+				enum ofono_atom_watch_condition cond,
+				void *data)
+{
+	struct push_notification *pn = data;
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+		g_dbus_unregister_interface(conn,
+					ofono_modem_get_path(pn->modem),
+					PUSH_NOTIFICATION_INTERFACE);
+		return;
+	}
+
+	DBG("registered");
+	pn->sms = __ofono_atom_get_data(atom);
+
+	if (!g_dbus_register_interface(conn, ofono_modem_get_path(pn->modem),
+					PUSH_NOTIFICATION_INTERFACE,
+					push_notification_methods, NULL, NULL,
+					pn, push_notification_cleanup)) {
+		ofono_error("Could not create %s interface",
+				PUSH_NOTIFICATION_INTERFACE);
+
+		return;
+	}
+
+	ofono_modem_add_interface(pn->modem, PUSH_NOTIFICATION_INTERFACE);
+}
+
+static void modem_watch(struct ofono_modem *modem, gboolean added, void *user)
+{
+	struct push_notification *pn;
+	DBG("modem: %p, added: %d", modem, added);
+
+	if (added == FALSE)
+		return;
+
+	pn = g_try_new0(struct push_notification, 1);
+	if (pn == NULL)
+		return;
+
+	pn->modem = modem;
+	__ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SMS,
+					sms_watch, pn, g_free);
+}
+
+static void call_modemwatch(struct ofono_modem *modem, void *user)
+{
+	modem_watch(modem, TRUE, user);
+}
+
+static int push_notification_init()
+{
+	DBG("");
+
+	modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL);
+
+	__ofono_modem_foreach(call_modemwatch, NULL);
+
+	return 0;
+}
+
+static void push_notification_exit()
+{
+	DBG("");
+
+	__ofono_modemwatch_remove(modemwatch_id);
+}
+
+OFONO_PLUGIN_DEFINE(push_notification, "Push Notification Plugin", VERSION,
+			OFONO_PLUGIN_PRIORITY_DEFAULT,
+			push_notification_init, push_notification_exit)
--- plugins/smart-messaging.c
+++ plugins/smart-messaging.c
@@ -35,38 +35,236 @@
 #include <ofono/log.h>
 #include <ofono/modem.h>
 #include <ofono/dbus.h>
+#include "smsagent.h"
+#include "smsutil.h"
+#include "common.h"
 
 #define SMART_MESSAGING_INTERFACE "org.ofono.SmartMessaging"
+#define AGENT_INTERFACE "org.ofono.SmartMessagingAgent"
+
+#define VCARD_SRC_PORT -1
+#define VCARD_DST_PORT 9204
+
+#define VCAL_SRC_PORT -1
+#define VCAL_DST_PORT 9205
 
 static unsigned int modemwatch_id;
 
 struct smart_messaging {
 	struct ofono_modem *modem;
 	struct ofono_sms *sms;
+	struct sms_agent *agent;
+	unsigned int vcard_watch;
+	unsigned int vcal_watch;
 };
 
+static void agent_exited(void *userdata)
+{
+	struct smart_messaging *sm = userdata;
+
+	if (sm->vcard_watch > 0) {
+		__ofono_sms_datagram_watch_remove(sm->sms, sm->vcard_watch);
+		sm->vcard_watch = 0;
+	}
+
+	if (sm->vcal_watch > 0) {
+		__ofono_sms_datagram_watch_remove(sm->sms, sm->vcal_watch);
+		sm->vcal_watch = 0;
+	}
+
+	sm->agent = NULL;
+}
+
+static void vcard_received(const char *from, const struct tm *remote,
+				const struct tm *local, int dst, int src,
+				const unsigned char *buffer,
+				unsigned int len, void *data)
+{
+	struct smart_messaging *sm = data;
+
+	if (sm->agent == NULL)
+		return;
+
+	sms_agent_dispatch_datagram(sm->agent, "ReceiveBusinessCard",
+					from, remote, local, buffer, len,
+					NULL, NULL, NULL);
+}
+
+static void vcal_received(const char *from, const struct tm *remote,
+				const struct tm *local, int dst, int src,
+				const unsigned char *buffer,
+				unsigned int len, void *data)
+{
+	struct smart_messaging *sm = data;
+
+	if (sm->agent == NULL)
+		return;
+
+	sms_agent_dispatch_datagram(sm->agent, "ReceiveAppointment",
+					from, remote, local, buffer, len,
+					NULL, NULL, NULL);
+}
+
 static DBusMessage *smart_messaging_register_agent(DBusConnection *conn,
 					DBusMessage *msg, void *data)
 {
-	return __ofono_error_not_implemented(msg);
+	struct smart_messaging *sm = data;
+	const char *agent_path;
+
+	if (sm->agent)
+		return __ofono_error_busy(msg);
+
+	if (dbus_message_get_args(msg, NULL,
+					DBUS_TYPE_OBJECT_PATH, &agent_path,
+					DBUS_TYPE_INVALID) == FALSE)
+		return __ofono_error_invalid_args(msg);
+
+	if (!__ofono_dbus_valid_object_path(agent_path))
+		return __ofono_error_invalid_format(msg);
+
+	sm->agent = sms_agent_new(AGENT_INTERFACE,
+					dbus_message_get_sender(msg),
+					agent_path);
+
+	if (sm->agent == NULL)
+		return __ofono_error_failed(msg);
+
+	sms_agent_set_removed_notify(sm->agent, agent_exited, sm);
+
+	sm->vcard_watch = __ofono_sms_datagram_watch_add(sm->sms,
+							vcard_received,
+							VCARD_DST_PORT,
+							VCARD_SRC_PORT,
+							sm, NULL);
+
+	sm->vcal_watch = __ofono_sms_datagram_watch_add(sm->sms,
+							vcal_received,
+							VCAL_DST_PORT,
+							VCAL_SRC_PORT,
+							sm, NULL);
+
+	return dbus_message_new_method_return(msg);
 }
 
 static DBusMessage *smart_messaging_unregister_agent(DBusConnection *conn,
 					DBusMessage *msg, void *data)
 {
-	return __ofono_error_not_implemented(msg);
+	struct smart_messaging *sm = data;
+	const char *agent_path;
+	const char *agent_bus = dbus_message_get_sender(msg);
+
+	if (dbus_message_get_args(msg, NULL,
+					DBUS_TYPE_OBJECT_PATH, &agent_path,
+					DBUS_TYPE_INVALID) == FALSE)
+		return __ofono_error_invalid_args(msg);
+
+	if (sm->agent == NULL)
+		return __ofono_error_failed(msg);
+
+	if (sms_agent_matches(sm->agent, agent_bus, agent_path) == FALSE)
+		return __ofono_error_failed(msg);
+
+	sms_agent_free(sm->agent);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static void message_queued(struct ofono_sms *sms,
+				const struct ofono_uuid *uuid, void *data)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	DBusMessage *msg = data;
+	const char *path;
+
+	path = __ofono_sms_message_path_from_uuid(sms, uuid);
+	g_dbus_send_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_INVALID);
 }
 
 static DBusMessage *smart_messaging_send_vcard(DBusConnection *conn,
 					DBusMessage *msg, void *data)
 {
-	return __ofono_error_not_implemented(msg);
+	struct smart_messaging *sm = data;
+	const char *to;
+	unsigned char *bytes;
+	int len;
+	GSList *msg_list;
+	unsigned int flags;
+	gboolean use_16bit_ref = FALSE;
+	int err;
+	struct ofono_uuid uuid;
+	unsigned short ref;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &to,
+					DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+					&bytes, &len, DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	if (valid_phone_number_format(to) == FALSE)
+		return __ofono_error_invalid_format(msg);
+
+	ref = __ofono_sms_get_next_ref(sm->sms);
+	msg_list = sms_datagram_prepare(to, bytes, len, ref, use_16bit_ref,
+						0, VCARD_DST_PORT, TRUE, FALSE);
+
+	if (!msg_list)
+		return __ofono_error_invalid_format(msg);
+
+	flags = OFONO_SMS_SUBMIT_FLAG_RETRY | OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS;
+
+	err = __ofono_sms_txq_submit(sm->sms, msg_list, flags, &uuid,
+					message_queued, msg);
+
+	g_slist_foreach(msg_list, (GFunc)g_free, NULL);
+	g_slist_free(msg_list);
+
+	if (err < 0)
+		return __ofono_error_failed(msg);
+
+	return NULL;
 }
 
 static DBusMessage *smart_messaging_send_vcal(DBusConnection *conn,
 					DBusMessage *msg, void *data)
 {
-	return __ofono_error_not_implemented(msg);
+	struct smart_messaging *sm = data;
+	const char *to;
+	unsigned char *bytes;
+	int len;
+	GSList *msg_list;
+	unsigned int flags;
+	gboolean use_16bit_ref = FALSE;
+	int err;
+	struct ofono_uuid uuid;
+	unsigned short ref;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &to,
+					DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+					&bytes, &len, DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	if (valid_phone_number_format(to) == FALSE)
+		return __ofono_error_invalid_format(msg);
+
+	ref = __ofono_sms_get_next_ref(sm->sms);
+	msg_list = sms_datagram_prepare(to, bytes, len, ref, use_16bit_ref,
+						0, VCAL_DST_PORT, TRUE, FALSE);
+
+	if (!msg_list)
+		return __ofono_error_invalid_format(msg);
+
+	flags = OFONO_SMS_SUBMIT_FLAG_RETRY | OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS;
+
+	err = __ofono_sms_txq_submit(sm->sms, msg_list, flags, &uuid,
+					message_queued, msg);
+
+	g_slist_foreach(msg_list, (GFunc)g_free, NULL);
+	g_slist_free(msg_list);
+
+	if (err < 0)
+		return __ofono_error_failed(msg);
+
+	return NULL;
 }
 
 static GDBusMethodTable smart_messaging_methods[] = {
@@ -84,6 +282,14 @@
 	struct smart_messaging *sm = user;
 
 	DBG("%p", sm);
+
+	sm->vcard_watch = 0;
+	sm->vcal_watch = 0;
+	sm->sms = NULL;
+
+	sms_agent_free(sm->agent);
+
+	ofono_modem_remove_interface(sm->modem, SMART_MESSAGING_INTERFACE);
 }
 
 static void sms_watch(struct ofono_atom *atom,
@@ -94,15 +300,10 @@
 	DBusConnection *conn = ofono_dbus_get_connection();
 
 	if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
-		DBG("unregistered");
-		sm->sms = NULL;
-
 		g_dbus_unregister_interface(conn,
 					ofono_modem_get_path(sm->modem),
 					SMART_MESSAGING_INTERFACE);
 
-		ofono_modem_remove_interface(sm->modem,
-						SMART_MESSAGING_INTERFACE);
 		return;
 	}
 
--- plugins/zte.c
+++ plugins/zte.c
@@ -184,7 +184,8 @@
 	g_at_chat_send(data->aux, "ATE0 +CMEE=1", none_prefix,
 						NULL, NULL, NULL);
 
-	g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix,
+	/* Direct transition 0 -> 4 leaves SIM hosed */
+	g_at_chat_send(data->aux, "AT+CFUN=1;+CFUN=4", none_prefix,
 					cfun_enable, modem, NULL);
 
 	return -EINPROGRESS;
--- src/audio-settings.c
+++ src/audio-settings.c
@@ -135,7 +135,7 @@
 	if (!d || !d->probe)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -147,7 +147,7 @@
 	if (!d)
 		return;
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void audio_settings_unregister(struct ofono_atom *atom)
--- src/call-barring.c
+++ src/call-barring.c
@@ -1029,7 +1029,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -1038,7 +1038,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void call_barring_unregister(struct ofono_atom *atom)
--- src/call-forwarding.c
+++ src/call-forwarding.c
@@ -1112,7 +1112,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -1121,7 +1121,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void call_forwarding_unregister(struct ofono_atom *atom)
--- src/call-meter.c
+++ src/call-meter.c
@@ -667,7 +667,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -676,7 +676,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void call_meter_unregister(struct ofono_atom *atom)
--- src/call-settings.c
+++ src/call-settings.c
@@ -709,7 +709,7 @@
 
 	/* This is the temporary form of CLIR, handled in voicecalls */
 	if (!strlen(sia) && !strlen(sib) & !strlen(sic) &&
-		strlen(dn) && type != SS_CONTROL_TYPE_QUERY)
+			strlen(dn) && type != SS_CONTROL_TYPE_QUERY)
 		return FALSE;
 
 	if (strlen(sia) || strlen(sib) || strlen(sic) || strlen(dn)) {
@@ -719,8 +719,14 @@
 		return TRUE;
 	}
 
-	if ((type == SS_CONTROL_TYPE_QUERY && !cs->driver->clir_query) ||
-		(type != SS_CONTROL_TYPE_QUERY && !cs->driver->clir_set)) {
+	if (type == SS_CONTROL_TYPE_QUERY && cs->driver->clir_query == NULL) {
+		DBusMessage *reply = __ofono_error_not_implemented(msg);
+		g_dbus_send_message(conn, reply);
+
+		return TRUE;
+	}
+
+	if (type != SS_CONTROL_TYPE_QUERY && !cs->driver->clir_set) {
 		DBusMessage *reply = __ofono_error_not_implemented(msg);
 		g_dbus_send_message(conn, reply);
 
@@ -734,7 +740,7 @@
 	case SS_CONTROL_TYPE_REGISTRATION:
 	case SS_CONTROL_TYPE_ACTIVATION:
 		cs->ss_req_type = SS_CONTROL_TYPE_ACTIVATION;
-		cs->driver->clir_set(cs, OFONO_CLIR_OPTION_INVOCATION,
+		cs->driver->clir_set(cs, OFONO_CLIR_OPTION_SUPPRESSION,
 					clir_ss_set_callback, cs);
 		break;
 
@@ -746,7 +752,7 @@
 	case SS_CONTROL_TYPE_DEACTIVATION:
 	case SS_CONTROL_TYPE_ERASURE:
 		cs->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION;
-		cs->driver->clir_set(cs, OFONO_CLIR_OPTION_SUPPRESSION,
+		cs->driver->clir_set(cs, OFONO_CLIR_OPTION_INVOCATION,
 					clir_ss_set_callback, cs);
 		break;
 	};
@@ -1193,7 +1199,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -1202,7 +1208,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void call_settings_unregister(struct ofono_atom *atom)
--- src/cbs.c
+++ src/cbs.c
@@ -173,6 +173,7 @@
 void ofono_cbs_notify(struct ofono_cbs *cbs, const unsigned char *pdu,
 				int pdu_len)
 {
+	struct ofono_modem *modem = __ofono_atom_get_modem(cbs->atom);
 	struct cbs c;
 	enum sms_class cls;
 	gboolean udhi;
@@ -191,8 +192,21 @@
 	}
 
 	if (cbs_topic_in_range(c.message_identifier, cbs->efcbmid_contents)) {
+		struct ofono_atom *sim_atom;
+
+		sim_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_SIM);
+		if (!sim_atom)
+			return;
+
+		if (!__ofono_sim_service_available(
+					__ofono_atom_get_data(sim_atom),
+					SIM_UST_SERVICE_DATA_DOWNLOAD_SMS_CB,
+					SIM_SST_SERVICE_DATA_DOWNLOAD_SMS_CB))
+			return;
+
 		if (cbs->stk)
 			__ofono_cbs_sim_download(cbs->stk, &c);
+
 		return;
 	}
 
@@ -551,7 +565,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -560,7 +574,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void cbs_unregister(struct ofono_atom *atom)
@@ -574,20 +588,20 @@
 	ofono_modem_remove_interface(modem, OFONO_CELL_BROADCAST_INTERFACE);
 
 	if (cbs->topics) {
-		g_slist_foreach(cbs->topics, (GFunc)g_free, NULL);
+		g_slist_foreach(cbs->topics, (GFunc) g_free, NULL);
 		g_slist_free(cbs->topics);
 		cbs->topics = NULL;
 	}
 
 	if (cbs->new_topics) {
-		g_slist_foreach(cbs->new_topics, (GFunc)g_free, NULL);
+		g_slist_foreach(cbs->new_topics, (GFunc) g_free, NULL);
 		g_slist_free(cbs->new_topics);
 		cbs->new_topics = NULL;
 	}
 
 	if (cbs->efcbmid_length) {
 		cbs->efcbmid_length = 0;
-		g_slist_foreach(cbs->efcbmid_contents, (GFunc)g_free, NULL);
+		g_slist_foreach(cbs->efcbmid_contents, (GFunc) g_free, NULL);
 		g_slist_free(cbs->efcbmid_contents);
 		cbs->efcbmid_contents = NULL;
 	}
--- src/common.c
+++ src/common.c
@@ -396,7 +396,7 @@
 
 void string_to_phone_number(const char *str, struct ofono_phone_number *ph)
 {
-	if (strlen(str) && str[0] == '+') {
+	if (str[0] == '+') {
 		strcpy(ph->number, str+1);
 		ph->type = 145;	/* International */
 	} else {
--- src/dbus.c
+++ src/dbus.c
@@ -28,7 +28,7 @@
 
 #include "ofono.h"
 
-#define DBUS_GSM_ERROR_INTERFACE "org.ofono.Error"
+#define OFONO_ERROR_INTERFACE "org.ofono.Error"
 
 static DBusConnection *g_connection;
 
@@ -75,7 +75,7 @@
 	DBusMessageIter variant, array;
 	char typesig[2];
 	char arraysig[3];
-	const char **str_array = *(const char ***)val;
+	const char **str_array = *(const char ***) val;
 	int i;
 
 	arraysig[0] = DBUS_TYPE_ARRAY;
@@ -117,7 +117,7 @@
 	DBusMessageIter variant, array, entry;
 	char typesig[5];
 	char arraysig[6];
-	const void **val_array = *(const void ***)val;
+	const void **val_array = *(const void ***) val;
 	int i;
 
 	arraysig[0] = DBUS_TYPE_ARRAY;
@@ -247,52 +247,52 @@
 
 DBusMessage *__ofono_error_invalid_args(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE
 					".InvalidArguments",
 					"Invalid arguments in method call");
 }
 
 DBusMessage *__ofono_error_invalid_format(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE
 					".InvalidFormat",
 					"Argument format is not recognized");
 }
 
 DBusMessage *__ofono_error_not_implemented(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE
 					".NotImplemented",
 					"Implementation not provided");
 }
 
 DBusMessage *__ofono_error_failed(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".Failed",
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".Failed",
 					"Operation failed");
 }
 
 DBusMessage *__ofono_error_busy(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".InProgress",
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".InProgress",
 					"Operation already in progress");
 }
 
 DBusMessage *__ofono_error_not_found(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".NotFound",
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotFound",
 			"Object is not found or not valid for this operation");
 }
 
 DBusMessage *__ofono_error_not_active(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".NotActive",
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotActive",
 			"Operation is not active or in progress");
 }
 
 DBusMessage *__ofono_error_not_supported(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE
 					".NotSupported",
 					"Operation is not supported by the"
 					" network / modem");
@@ -300,48 +300,54 @@
 
 DBusMessage *__ofono_error_not_available(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE
 					".NotAvailable",
 					"Operation currently not available");
 }
 
 DBusMessage *__ofono_error_timed_out(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".Timedout",
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".Timedout",
 			"Operation failure due to timeout");
 }
 
 DBusMessage *__ofono_error_sim_not_ready(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".SimNotReady",
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".SimNotReady",
 			"SIM is not ready or not inserted");
 }
 
 DBusMessage *__ofono_error_in_use(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".InUse",
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".InUse",
 			"The resource is currently in use");
 }
 
 DBusMessage *__ofono_error_not_attached(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".NotAttached",
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotAttached",
 			"GPRS is not attached");
 }
 
 DBusMessage *__ofono_error_attach_in_progress(DBusMessage *msg)
 {
 	return g_dbus_create_error(msg,
-				DBUS_GSM_ERROR_INTERFACE ".AttachInProgress",
+				OFONO_ERROR_INTERFACE ".AttachInProgress",
 				"GPRS Attach is in progress");
 }
 
 DBusMessage *__ofono_error_canceled(DBusMessage *msg)
 {
-	return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".Canceled",
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".Canceled",
 					"Operation has been canceled");
 }
 
+DBusMessage *__ofono_error_access_denied(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".AccessDenied",
+					"Operation not permitted");
+}
+
 void __ofono_dbus_pending_reply(DBusMessage **msg, DBusMessage *reply)
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
--- src/gprs.c
+++ src/gprs.c
@@ -27,9 +27,13 @@
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
+#include <stdlib.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include <glib.h>
 #include <gdbus.h>
@@ -46,19 +50,14 @@
 #define SETTINGS_STORE "gprs"
 #define SETTINGS_GROUP "Settings"
 #define MAX_CONTEXT_NAME_LENGTH 127
+#define MAX_MESSAGE_PROXY_LENGTH 255
+#define MAX_MESSAGE_CENTER_LENGTH 255
 #define MAX_CONTEXTS 256
 #define SUSPEND_TIMEOUT 8
 
 static GSList *g_drivers = NULL;
 static GSList *g_context_drivers = NULL;
 
-enum gprs_context_type {
-	GPRS_CONTEXT_TYPE_INTERNET = 0,
-	GPRS_CONTEXT_TYPE_MMS,
-	GPRS_CONTEXT_TYPE_WAP,
-	GPRS_CONTEXT_TYPE_INVALID,
-};
-
 struct ofono_gprs {
 	GSList *contexts;
 	ofono_bool_t attached;
@@ -79,7 +78,7 @@
 	GKeyFile *settings;
 	char *imsi;
 	DBusMessage *pending;
-	struct ofono_gprs_context *context_driver;
+	GSList *context_drivers;
 	const struct ofono_gprs_driver *driver;
 	void *driver_data;
 	struct ofono_atom *atom;
@@ -87,59 +86,99 @@
 
 struct ofono_gprs_context {
 	struct ofono_gprs *gprs;
-	DBusMessage *pending;
+	enum ofono_gprs_context_type type;
+	ofono_bool_t inuse;
 	const struct ofono_gprs_context_driver *driver;
 	void *driver_data;
 	struct ofono_atom *atom;
 };
 
 struct context_settings {
+	enum ofono_gprs_context_type type;
 	char *interface;
 	gboolean static_ip;
 	char *ip;
 	char *netmask;
 	char *gateway;
 	char **dns;
+	char *proxy;
 };
 
 struct pri_context {
 	ofono_bool_t active;
-	enum gprs_context_type type;
+	enum ofono_gprs_context_type type;
 	char name[MAX_CONTEXT_NAME_LENGTH + 1];
+	char message_proxy[MAX_MESSAGE_PROXY_LENGTH + 1];
+	char message_center[MAX_MESSAGE_CENTER_LENGTH + 1];
 	unsigned int id;
 	char *path;
 	char *key;
 	struct context_settings *settings;
+	char *proxy_host;
+	uint16_t proxy_port;
+	DBusMessage *pending;
 	struct ofono_gprs_primary_context context;
+	struct ofono_gprs_context *context_driver;
 	struct ofono_gprs *gprs;
 };
 
 static void gprs_netreg_update(struct ofono_gprs *gprs);
+static void gprs_deactivate_next(struct ofono_gprs *gprs);
+
+static const char *gprs_context_default_name(enum ofono_gprs_context_type type)
+{
+	switch (type) {
+	case OFONO_GPRS_CONTEXT_TYPE_ANY:
+		return NULL;
+	case OFONO_GPRS_CONTEXT_TYPE_INTERNET:
+		return "Internet";
+	case OFONO_GPRS_CONTEXT_TYPE_MMS:
+		return "MMS";
+	case OFONO_GPRS_CONTEXT_TYPE_WAP:
+		return "WAP";
+	case OFONO_GPRS_CONTEXT_TYPE_IMS:
+		return "IMS";
+	}
+
+	return NULL;
+}
 
-static const char *gprs_context_type_to_string(int type)
+static const char *gprs_context_type_to_string(enum ofono_gprs_context_type type)
 {
 	switch (type) {
-	case GPRS_CONTEXT_TYPE_INTERNET:
+	case OFONO_GPRS_CONTEXT_TYPE_ANY:
+		return NULL;
+	case OFONO_GPRS_CONTEXT_TYPE_INTERNET:
 		return "internet";
-	case GPRS_CONTEXT_TYPE_MMS:
+	case OFONO_GPRS_CONTEXT_TYPE_MMS:
 		return "mms";
-	case GPRS_CONTEXT_TYPE_WAP:
+	case OFONO_GPRS_CONTEXT_TYPE_WAP:
 		return "wap";
+	case OFONO_GPRS_CONTEXT_TYPE_IMS:
+		return "ims";
 	}
 
 	return NULL;
 }
 
-static enum gprs_context_type gprs_context_string_to_type(const char *str)
+static gboolean gprs_context_string_to_type(const char *str,
+					enum ofono_gprs_context_type *out)
 {
-	if (g_str_equal(str, "internet"))
-		return GPRS_CONTEXT_TYPE_INTERNET;
-	else if (g_str_equal(str, "wap"))
-		return GPRS_CONTEXT_TYPE_WAP;
-	else if (g_str_equal(str, "mms"))
-		return GPRS_CONTEXT_TYPE_MMS;
+	if (g_str_equal(str, "internet")) {
+		*out = OFONO_GPRS_CONTEXT_TYPE_INTERNET;
+		return TRUE;
+	} else if (g_str_equal(str, "wap")) {
+		*out = OFONO_GPRS_CONTEXT_TYPE_WAP;
+		return TRUE;
+	} else if (g_str_equal(str, "mms")) {
+		*out = OFONO_GPRS_CONTEXT_TYPE_MMS;
+		return TRUE;
+	} else if (g_str_equal(str, "ims")) {
+		*out = OFONO_GPRS_CONTEXT_TYPE_IMS;
+		return FALSE;
+	}
 
-	return GPRS_CONTEXT_TYPE_INVALID;
+	return FALSE;
 }
 
 static const char *gprs_proto_to_string(enum ofono_gprs_proto proto)
@@ -200,6 +239,7 @@
 	g_free(settings->netmask);
 	g_free(settings->gateway);
 	g_strfreev(settings->dns);
+	g_free(settings->proxy);
 
 	g_free(settings);
 }
@@ -226,11 +266,18 @@
 	dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
 						typesig, &array);
 	if (settings == NULL)
-		goto end;
+		goto done;
 
 	ofono_dbus_dict_append(&array, "Interface",
 				DBUS_TYPE_STRING, &settings->interface);
 
+	if (settings->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+		if (settings->proxy)
+			ofono_dbus_dict_append(&array, "Proxy",
+					DBUS_TYPE_STRING, &settings->proxy);
+		goto done;
+	}
+
 	if (settings->static_ip == TRUE)
 		method = "static";
 	else
@@ -255,7 +302,7 @@
 						DBUS_TYPE_STRING,
 						&settings->dns);
 
-end:
+done:
 	dbus_message_iter_close_container(&variant, &array);
 
 	dbus_message_iter_close_container(iter, &variant);
@@ -301,6 +348,53 @@
 	g_dbus_send_message(conn, signal);
 }
 
+static void pri_parse_proxy(struct pri_context *ctx, const char *proxy)
+{
+	char *scheme, *host, *port, *path;
+
+	scheme = g_strdup(proxy);
+	if (scheme == NULL)
+		return;
+
+	host = strstr(scheme, "://");
+	if (host != NULL) {
+		*host = '\0';
+		host += 3;
+
+		if (strcasecmp(scheme, "https") == 0)
+			ctx->proxy_port = 443;
+		else if (strcasecmp(scheme, "http") == 0)
+			ctx->proxy_port = 80;
+		else {
+			g_free(scheme);
+			return;
+		}
+	} else {
+		host = scheme;
+		ctx->proxy_port = 80;
+	}
+
+	path = strchr(host, '/');
+	if (path != NULL)
+		*(path++) = '\0';
+
+	port = strrchr(host, ':');
+	if (port != NULL) {
+		char *end;
+		int tmp = strtol(port + 1, &end, 10);
+
+		if (*end == '\0') {
+			*port = '\0';
+			ctx->proxy_port = tmp;
+		}
+	}
+
+	g_free(ctx->proxy_host);
+	ctx->proxy_host = g_strdup(host);
+
+	g_free(scheme);
+}
+
 static void pri_ifupdown(const char *interface, ofono_bool_t active)
 {
 	struct ifreq ifr;
@@ -336,6 +430,88 @@
 	close(sk);
 }
 
+static void pri_setaddr(const char *interface, const char *address)
+{
+	struct ifreq ifr;
+	struct sockaddr_in addr;
+	int sk;
+
+	if (!interface)
+		return;
+
+	sk = socket(PF_INET, SOCK_DGRAM, 0);
+	if (sk < 0)
+		return;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, interface, IFNAMSIZ);
+
+	if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0)
+		goto done;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = address ? inet_addr(address) : INADDR_ANY;
+	memcpy(&ifr.ifr_addr, &addr, sizeof(ifr.ifr_addr));
+
+	if (ioctl(sk, SIOCSIFADDR, &ifr) < 0) {
+		ofono_error("Failed to set interface address");
+		goto done;
+	}
+
+	if (!address)
+		goto done;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = inet_addr("255.255.255.255");
+	memcpy(&ifr.ifr_netmask, &addr, sizeof(ifr.ifr_netmask));
+
+	if (ioctl(sk, SIOCSIFNETMASK, &ifr) < 0)
+		ofono_error("Failed to set interface netmask");
+
+done:
+	close(sk);
+}
+
+static void pri_setproxy(const char *interface, const char *proxy)
+{
+	struct rtentry rt;
+	struct sockaddr_in addr;
+	int sk;
+
+	if (!interface)
+		return;
+
+	sk = socket(PF_INET, SOCK_DGRAM, 0);
+	if (sk < 0)
+		return;
+
+	memset(&rt, 0, sizeof(rt));
+	rt.rt_flags = RTF_UP | RTF_HOST;
+	rt.rt_dev = (char *) interface;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = inet_addr(proxy);
+	memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = INADDR_ANY;
+	memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = INADDR_ANY;
+	memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+
+	if (ioctl(sk, SIOCADDRT, &rt) < 0)
+		ofono_error("Failed to add proxy host route");
+
+	close(sk);
+}
+
 static void pri_reset_context_settings(struct pri_context *ctx)
 {
 	char *interface;
@@ -351,6 +527,14 @@
 
 	pri_context_signal_settings(ctx);
 
+	if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+		pri_setaddr(interface, NULL);
+
+		g_free(ctx->proxy_host);
+		ctx->proxy_host = NULL;
+		ctx->proxy_port = 0;
+	}
+
 	pri_ifupdown(interface, FALSE);
 
 	g_free(interface);
@@ -365,7 +549,11 @@
 	if (ctx->settings)
 		context_settings_free(ctx->settings);
 
-	ctx->settings = g_new0(struct context_settings, 1);
+	ctx->settings = g_try_new0(struct context_settings, 1);
+	if (!ctx->settings)
+		return;
+
+	ctx->settings->type = ctx->type;
 
 	ctx->settings->interface = g_strdup(interface);
 	ctx->settings->static_ip = static_ip;
@@ -374,8 +562,22 @@
 	ctx->settings->gateway = g_strdup(gateway);
 	ctx->settings->dns = g_strdupv((char **)dns);
 
+	if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS && ctx->message_proxy)
+		ctx->settings->proxy = g_strdup(ctx->message_proxy);
+
 	pri_ifupdown(interface, TRUE);
 
+	if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+		pri_parse_proxy(ctx, ctx->message_proxy);
+
+		DBG("proxy %s port %u", ctx->proxy_host, ctx->proxy_port);
+
+		pri_setaddr(interface, ip);
+
+		if (ctx->proxy_host)
+			pri_setproxy(interface, ctx->proxy_host);
+	}
+
 	pri_context_signal_settings(ctx);
 }
 
@@ -409,6 +611,16 @@
 	ofono_dbus_dict_append(dict, "Password", DBUS_TYPE_STRING,
 				&strvalue);
 
+	if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+		strvalue = ctx->message_proxy;
+		ofono_dbus_dict_append(dict, "MessageProxy",
+					DBUS_TYPE_STRING, &strvalue);
+
+		strvalue = ctx->message_center;
+		ofono_dbus_dict_append(dict, "MessageCenter",
+					DBUS_TYPE_STRING, &strvalue);
+	}
+
 	context_settings_append_dict(ctx->settings, dict);
 }
 
@@ -443,7 +655,6 @@
 					void *data)
 {
 	struct pri_context *ctx = data;
-	struct ofono_gprs_context *gc = ctx->gprs->context_driver;
 	DBusConnection *conn = ofono_dbus_get_connection();
 	dbus_bool_t value;
 
@@ -452,18 +663,20 @@
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		DBG("Activating context failed with error: %s",
 				telephony_error_to_str(error));
-		__ofono_dbus_pending_reply(&gc->pending,
-					__ofono_error_failed(gc->pending));
+		__ofono_dbus_pending_reply(&ctx->pending,
+					__ofono_error_failed(ctx->pending));
 
 		gprs_cid_release(ctx->gprs, ctx->context.cid);
 		ctx->context.cid = 0;
+		ctx->context_driver->inuse = FALSE;
+		ctx->context_driver = NULL;
 
 		return;
 	}
 
 	ctx->active = TRUE;
-	__ofono_dbus_pending_reply(&gc->pending,
-				dbus_message_new_method_return(gc->pending));
+	__ofono_dbus_pending_reply(&ctx->pending,
+				dbus_message_new_method_return(ctx->pending));
 
 	/*
 	 * If we don't have the interface, don't bother emitting any settings,
@@ -482,24 +695,25 @@
 static void pri_deactivate_callback(const struct ofono_error *error, void *data)
 {
 	struct pri_context *ctx = data;
-	struct ofono_gprs_context *gc = ctx->gprs->context_driver;
 	DBusConnection *conn = ofono_dbus_get_connection();
 	dbus_bool_t value;
 
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		DBG("Deactivating context failed with error: %s",
 				telephony_error_to_str(error));
-		__ofono_dbus_pending_reply(&gc->pending,
-					__ofono_error_failed(gc->pending));
+		__ofono_dbus_pending_reply(&ctx->pending,
+					__ofono_error_failed(ctx->pending));
 		return;
 	}
 
 	gprs_cid_release(ctx->gprs, ctx->context.cid);
 	ctx->context.cid = 0;
-
 	ctx->active = FALSE;
-	__ofono_dbus_pending_reply(&gc->pending,
-				dbus_message_new_method_return(gc->pending));
+	ctx->context_driver->inuse = FALSE;
+	ctx->context_driver = NULL;
+
+	__ofono_dbus_pending_reply(&ctx->pending,
+				dbus_message_new_method_return(ctx->pending));
 
 	pri_reset_context_settings(ctx);
 
@@ -605,11 +819,9 @@
 					DBusMessage *msg, const char *type)
 {
 	GKeyFile *settings = ctx->gprs->settings;
-	enum gprs_context_type context_type;
+	enum ofono_gprs_context_type context_type;
 
-	context_type = gprs_context_string_to_type(type);
-
-	if (context_type == GPRS_CONTEXT_TYPE_INVALID)
+	if (gprs_context_string_to_type(type, &context_type) == FALSE)
 		return __ofono_error_invalid_format(msg);
 
 	if (ctx->type == context_type)
@@ -687,6 +899,96 @@
 	return NULL;
 }
 
+static DBusMessage *pri_set_message_proxy(struct pri_context *ctx,
+					DBusConnection *conn,
+					DBusMessage *msg, const char *proxy)
+{
+	GKeyFile *settings = ctx->gprs->settings;
+
+	if (strlen(proxy) > MAX_MESSAGE_PROXY_LENGTH)
+		return __ofono_error_invalid_format(msg);
+
+	if (ctx->message_proxy && g_str_equal(ctx->message_proxy, proxy))
+		return dbus_message_new_method_return(msg);
+
+	strcpy(ctx->message_proxy, proxy);
+
+	if (settings) {
+		g_key_file_set_string(settings, ctx->key, "MessageProxy",
+							ctx->message_proxy);
+		storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings);
+	}
+
+	g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+	ofono_dbus_signal_property_changed(conn, ctx->path,
+				OFONO_CONNECTION_CONTEXT_INTERFACE,
+				"MessageProxy", DBUS_TYPE_STRING, &proxy);
+
+	return NULL;
+}
+
+static DBusMessage *pri_set_message_center(struct pri_context *ctx,
+					DBusConnection *conn,
+					DBusMessage *msg, const char *center)
+{
+	GKeyFile *settings = ctx->gprs->settings;
+
+	if (strlen(center) > MAX_MESSAGE_CENTER_LENGTH)
+		return __ofono_error_invalid_format(msg);
+
+	if (ctx->message_center && g_str_equal(ctx->message_center, center))
+		return dbus_message_new_method_return(msg);
+
+	strcpy(ctx->message_center, center);
+
+	if (settings) {
+		g_key_file_set_string(settings, ctx->key, "MessageCenter",
+							ctx->message_center);
+		storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings);
+	}
+
+	g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+	ofono_dbus_signal_property_changed(conn, ctx->path,
+				OFONO_CONNECTION_CONTEXT_INTERFACE,
+				"MessageCenter", DBUS_TYPE_STRING, &center);
+
+	return NULL;
+}
+
+static gboolean assign_context(struct pri_context *ctx)
+{
+	struct idmap *cidmap = ctx->gprs->cid_map;
+	unsigned int cid_min;
+	GSList *l;
+
+	if (cidmap == NULL)
+		return FALSE;
+
+	cid_min = idmap_get_min(cidmap);
+
+	ctx->context.cid = gprs_cid_alloc(ctx->gprs);
+	if (ctx->context.cid == 0)
+		return FALSE;
+
+	for (l = ctx->gprs->context_drivers; l; l = l->next) {
+		struct ofono_gprs_context *gc = l->data;
+
+		if (gc->inuse == TRUE)
+			continue;
+
+		if (gc->type == OFONO_GPRS_CONTEXT_TYPE_ANY ||
+						gc->type == ctx->type) {
+			ctx->context_driver = gc;
+			ctx->context_driver->inuse = TRUE;
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
 static DBusMessage *pri_set_property(DBusConnection *conn,
 					DBusMessage *msg, void *data)
 {
@@ -712,14 +1014,12 @@
 	dbus_message_iter_recurse(&iter, &var);
 
 	if (g_str_equal(property, "Active")) {
-		struct ofono_gprs_context *gc = ctx->gprs->context_driver;
+		struct ofono_gprs_context *gc;
 
-		if (gc == NULL || gc->driver->activate_primary == NULL ||
-				gc->driver->deactivate_primary == NULL ||
-				ctx->gprs->cid_map == NULL)
-			return __ofono_error_not_implemented(msg);
+		if (ctx->gprs->pending)
+			return __ofono_error_busy(msg);
 
-		if (gc->pending)
+		if (ctx->pending)
 			return __ofono_error_busy(msg);
 
 		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
@@ -737,24 +1037,17 @@
 			return __ofono_error_attach_in_progress(msg);
 
 		if (value) {
-			ctx->context.cid = gprs_cid_alloc(ctx->gprs);
-
-			if (ctx->context.cid == 0)
-				return __ofono_error_failed(msg);
-
-			if (ctx->context.cid !=
-					idmap_get_min(ctx->gprs->cid_map)) {
-				ofono_error("Multiple active contexts are"
-						" not yet supported");
-
-				gprs_cid_release(ctx->gprs, ctx->context.cid);
-				ctx->context.cid = 0;
-
-				return __ofono_error_failed(msg);
-			}
+			if (assign_context(ctx) == FALSE)
+				return __ofono_error_not_implemented(msg);
 		}
 
-		gc->pending = dbus_message_ref(msg);
+		gc = ctx->context_driver;
+		if (gc == NULL || gc->driver == NULL ||
+				gc->driver->activate_primary == NULL ||
+				gc->driver->deactivate_primary == NULL)
+			return __ofono_error_not_implemented(msg);
+
+		ctx->pending = dbus_message_ref(msg);
 
 		if (value)
 			gc->driver->activate_primary(gc, &ctx->context,
@@ -814,6 +1107,25 @@
 		return pri_set_name(ctx, conn, msg, str);
 	}
 
+	if (ctx->type != OFONO_GPRS_CONTEXT_TYPE_MMS)
+		return __ofono_error_invalid_args(msg);
+
+	if (!strcmp(property, "MessageProxy")) {
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+			return __ofono_error_invalid_args(msg);
+
+		dbus_message_iter_get_basic(&var, &str);
+
+		return pri_set_message_proxy(ctx, conn, msg, str);
+	} else if (!strcmp(property, "MessageCenter")) {
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+			return __ofono_error_invalid_args(msg);
+
+		dbus_message_iter_get_basic(&var, &str);
+
+		return pri_set_message_center(ctx, conn, msg, str);
+	}
+
 	return __ofono_error_invalid_args(msg);
 }
 
@@ -830,14 +1142,20 @@
 };
 
 static struct pri_context *pri_context_create(struct ofono_gprs *gprs,
-						const char *name,
-						enum gprs_context_type type)
+					const char *name,
+					enum ofono_gprs_context_type type)
 {
 	struct pri_context *context = g_try_new0(struct pri_context, 1);
 
 	if (!context)
 		return NULL;
 
+	if (!name) {
+		name = gprs_context_default_name(type);
+		if (!name)
+			return NULL;
+	}
+
 	context->gprs = gprs;
 	strcpy(context->name, name);
 	context->type = type;
@@ -854,6 +1172,8 @@
 		ctx->settings = NULL;
 	}
 
+	g_free(ctx->proxy_host);
+
 	g_free(ctx->path);
 
 	g_free(ctx);
@@ -986,8 +1306,10 @@
 
 			gprs_cid_release(gprs, ctx->context.cid);
 			ctx->context.cid = 0;
-
 			ctx->active = FALSE;
+			ctx->context_driver->inuse = FALSE;
+			ctx->context_driver = NULL;
+
 			pri_reset_context_settings(ctx);
 
 			value = FALSE;
@@ -1227,7 +1549,7 @@
 
 static struct pri_context *add_context(struct ofono_gprs *gprs,
 					const char *name,
-					enum gprs_context_type type)
+					enum ofono_gprs_context_type type)
 {
 	unsigned int id;
 	struct pri_context *context;
@@ -1273,20 +1595,23 @@
 	struct ofono_gprs *gprs = data;
 	struct pri_context *context;
 	const char *typestr;
+	const char *name;
 	const char *path;
-	enum gprs_context_type type;
+	enum ofono_gprs_context_type type;
 	DBusMessage *signal;
 
 	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
 					DBUS_TYPE_INVALID))
 		return __ofono_error_invalid_args(msg);
 
-	type = gprs_context_string_to_type(typestr);
-
-	if (type == GPRS_CONTEXT_TYPE_INVALID)
+	if (gprs_context_string_to_type(typestr, &type) == FALSE)
 		return __ofono_error_invalid_format(msg);
 
-	context = add_context(gprs, typestr, type);
+	name = gprs_context_default_name(type);
+	if (name == NULL)
+		name = typestr;
+
+	context = add_context(gprs, name, type);
 	if (context == NULL)
 		return __ofono_error_failed(msg);
 
@@ -1373,6 +1698,9 @@
 	const char *path;
 	const char *atompath;
 
+	if (gprs->pending)
+		return __ofono_error_busy(msg);
+
 	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
 					DBUS_TYPE_INVALID))
 		return __ofono_error_invalid_args(msg);
@@ -1385,7 +1713,11 @@
 		return __ofono_error_not_found(msg);
 
 	if (ctx->active) {
-		struct ofono_gprs_context *gc = gprs->context_driver;
+		struct ofono_gprs_context *gc = ctx->context_driver;
+
+		/* This context is already being messed with */
+		if (ctx->pending)
+			return __ofono_error_busy(msg);
 
 		gprs->pending = dbus_message_ref(msg);
 		gc->driver->deactivate_primary(gc, ctx->context.cid,
@@ -1412,10 +1744,66 @@
 	return NULL;
 }
 
+static void gprs_deactivate_for_all(const struct ofono_error *error,
+					void *data)
+{
+	struct pri_context *ctx = data;
+	struct ofono_gprs *gprs = ctx->gprs;
+	DBusConnection *conn;
+	dbus_bool_t value;
+
+	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+		__ofono_dbus_pending_reply(&gprs->pending,
+					__ofono_error_failed(gprs->pending));
+		return;
+	}
+
+	gprs_cid_release(gprs, ctx->context.cid);
+	ctx->active = FALSE;
+	ctx->context.cid = 0;
+	ctx->context_driver->inuse = FALSE;
+	ctx->context_driver = NULL;
+
+	pri_reset_context_settings(ctx);
+
+	value = ctx->active;
+	conn = ofono_dbus_get_connection();
+	ofono_dbus_signal_property_changed(conn, ctx->path,
+					OFONO_CONNECTION_CONTEXT_INTERFACE,
+					"Active", DBUS_TYPE_BOOLEAN, &value);
+
+	gprs_deactivate_next(gprs);
+}
+
+static void gprs_deactivate_next(struct ofono_gprs *gprs)
+{
+	GSList *l;
+	struct pri_context *ctx;
+	struct ofono_gprs_context *gc;
+
+	for (l = gprs->contexts; l; l = l->next) {
+		ctx = l->data;
+
+		if (ctx->active == FALSE)
+			continue;
+
+		gc = ctx->context_driver;
+		gc->driver->deactivate_primary(gc, ctx->context.cid,
+					gprs_deactivate_for_all, ctx);
+
+		return;
+	}
+
+	__ofono_dbus_pending_reply(&gprs->pending,
+				dbus_message_new_method_return(gprs->pending));
+}
+
 static DBusMessage *gprs_deactivate_all(DBusConnection *conn,
 					DBusMessage *msg, void *data)
 {
 	struct ofono_gprs *gprs = data;
+	GSList *l;
+	struct pri_context *ctx;
 
 	if (gprs->pending)
 		return __ofono_error_busy(msg);
@@ -1423,7 +1811,18 @@
 	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID))
 		return __ofono_error_invalid_args(msg);
 
-	return __ofono_error_not_implemented(msg);
+	for (l = gprs->contexts; l; l = l->next) {
+		ctx = l->data;
+
+		if (ctx->pending)
+			return __ofono_error_busy(msg);
+	}
+
+	gprs->pending = dbus_message_ref(msg);
+
+	gprs_deactivate_next(gprs);
+
+	return NULL;
 }
 
 static DBusMessage *gprs_get_contexts(DBusConnection *conn,
@@ -1558,18 +1957,23 @@
 {
 	struct ofono_gprs_context *gc = __ofono_atom_get_data(atom);
 
-	if (gc->gprs)
-		gc->gprs->context_driver = NULL;
+	if (gc->gprs == NULL)
+		return;
 
+	gc->gprs->context_drivers = g_slist_remove(gc->gprs->context_drivers,
+							gc);
 	gc->gprs = NULL;
 }
 
 void ofono_gprs_add_context(struct ofono_gprs *gprs,
 				struct ofono_gprs_context *gc)
 {
-	gprs->context_driver = gc;
+	if (gc->driver == NULL)
+		return;
+
 	gc->gprs = gprs;
 
+	gprs->context_drivers = g_slist_append(gprs->context_drivers, gc);
 	__ofono_atom_register(gc->atom, gprs_context_unregister);
 }
 
@@ -1581,6 +1985,9 @@
 	struct pri_context *ctx;
 	dbus_bool_t value;
 
+	if (gc->gprs == NULL)
+		return;
+
 	for (l = gc->gprs->contexts; l; l = l->next) {
 		ctx = l->data;
 
@@ -1592,8 +1999,10 @@
 
 		gprs_cid_release(ctx->gprs, ctx->context.cid);
 		ctx->context.cid = 0;
-
 		ctx->active = FALSE;
+		ctx->context_driver->inuse = FALSE;
+		ctx->context_driver = NULL;
+
 		pri_reset_context_settings(ctx);
 
 		value = FALSE;
@@ -1610,7 +2019,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_context_drivers = g_slist_prepend(g_context_drivers, (void *)d);
+	g_context_drivers = g_slist_prepend(g_context_drivers, (void *) d);
 
 	return 0;
 }
@@ -1619,7 +2028,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_context_drivers = g_slist_remove(g_context_drivers, (void *)d);
+	g_context_drivers = g_slist_remove(g_context_drivers, (void *) d);
 }
 
 static void gprs_context_remove(struct ofono_atom *atom)
@@ -1648,10 +2057,11 @@
 		return NULL;
 
 	gc = g_try_new0(struct ofono_gprs_context, 1);
-
 	if (gc == NULL)
 		return NULL;
 
+	gc->type = OFONO_GPRS_CONTEXT_TYPE_ANY;
+
 	gc->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_GPRS_CONTEXT,
 						gprs_context_remove, gc);
 
@@ -1673,6 +2083,9 @@
 
 void ofono_gprs_context_remove(struct ofono_gprs_context *gc)
 {
+	if (gc == NULL)
+		return;
+
 	__ofono_atom_free(gc->atom);
 }
 
@@ -1691,6 +2104,14 @@
 	return __ofono_atom_get_modem(gc->atom);
 }
 
+void ofono_gprs_context_set_type(struct ofono_gprs_context *gc,
+                                        enum ofono_gprs_context_type type)
+{
+	DBG("type %d", type);
+
+	gc->type = type;
+}
+
 int ofono_gprs_driver_register(const struct ofono_gprs_driver *d)
 {
 	DBG("driver: %p, name: %s", d, d->name);
@@ -1775,10 +2196,7 @@
 		gprs->pid_map = NULL;
 	}
 
-	if (gprs->context_driver) {
-		gprs->context_driver->gprs = NULL;
-		gprs->context_driver = NULL;
-	}
+	g_slist_free(gprs->context_drivers);
 
 	if (gprs->driver && gprs->driver->remove)
 		gprs->driver->remove(gprs);
@@ -1797,7 +2215,6 @@
 		return NULL;
 
 	gprs = g_try_new0(struct ofono_gprs, 1);
-
 	if (gprs == NULL)
 		return NULL;
 
@@ -1852,10 +2269,12 @@
 	char *username = NULL;
 	char *password = NULL;
 	char *apn = NULL;
+	char *msgproxy = NULL;
+	char *msgcenter = NULL;
 	gboolean ret = FALSE;
 	gboolean legacy = FALSE;
 	struct pri_context *context;
-	enum gprs_context_type type;
+	enum ofono_gprs_context_type type;
 	enum ofono_gprs_proto proto;
 	unsigned int id;
 
@@ -1869,20 +2288,20 @@
 	if (id < 1 || id > MAX_CONTEXTS)
 		goto error;
 
-	if ((name = g_key_file_get_string(gprs->settings, group,
-					"Name", NULL)) == NULL)
+	name = g_key_file_get_string(gprs->settings, group, "Name", NULL);
+	if (name == NULL)
 		goto error;
 
-	if ((typestr = g_key_file_get_string(gprs->settings, group,
-					"Type", NULL)) == NULL)
+	typestr = g_key_file_get_string(gprs->settings, group, "Type", NULL);
+	if (typestr == NULL)
 		goto error;
 
-	type = gprs_context_string_to_type(typestr);
-	if (type == GPRS_CONTEXT_TYPE_INVALID)
+	if (gprs_context_string_to_type(typestr, &type) == FALSE)
 		goto error;
 
-	if ((protostr = g_key_file_get_string(gprs->settings, group,
-						"Protocol", NULL)) == NULL)
+	protostr = g_key_file_get_string(gprs->settings, group,
+							"Protocol", NULL);
+	if (protostr == NULL)
 		protostr = g_strdup("ip");
 
 	if (gprs_proto_from_string(protostr, &proto) == FALSE)
@@ -1890,7 +2309,7 @@
 
 	username = g_key_file_get_string(gprs->settings, group,
 						"Username", NULL);
-	if (!username)
+	if (username == NULL)
 		goto error;
 
 	if (strlen(username) > OFONO_GPRS_MAX_USERNAME_LENGTH)
@@ -1898,8 +2317,7 @@
 
 	password = g_key_file_get_string(gprs->settings, group,
 						"Password", NULL);
-
-	if (!password)
+	if (password == NULL)
 		goto error;
 
 	if (strlen(password) > OFONO_GPRS_MAX_PASSWORD_LENGTH)
@@ -1907,13 +2325,20 @@
 
 	apn = g_key_file_get_string(gprs->settings, group,
 					"AccessPointName", NULL);
-
-	if (!apn)
+	if (apn == NULL)
 		goto error;
 
 	if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH)
 		goto error;
 
+	if (type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+		msgproxy = g_key_file_get_string(gprs->settings, group,
+						"MessageProxy", NULL);
+
+		msgcenter = g_key_file_get_string(gprs->settings, group,
+						"MessageCenter", NULL);
+	}
+
 	/*
 	 * Accept empty (just created) APNs, but don't allow other
 	 * invalid ones
@@ -1921,7 +2346,8 @@
 	if (apn[0] != '\0' && is_valid_apn(apn) == FALSE)
 		goto error;
 
-	if ((context = pri_context_create(gprs, name, type)) == NULL)
+	context = pri_context_create(gprs, name, type);
+	if (context == NULL)
 		goto error;
 
 	idmap_take(gprs->pid_map, id);
@@ -1931,6 +2357,12 @@
 	strcpy(context->context.apn, apn);
 	context->context.proto = proto;
 
+	if (msgproxy != NULL)
+		strcpy(context->message_proxy, msgproxy);
+
+	if (msgcenter != NULL)
+		strcpy(context->message_center, msgcenter);
+
 	if (context_dbus_register(context) == FALSE)
 		goto error;
 
@@ -1951,6 +2383,8 @@
 	g_free(username);
 	g_free(password);
 	g_free(apn);
+	g_free(msgproxy);
+	g_free(msgcenter);
 
 	return ret;
 }
@@ -2051,7 +2485,7 @@
 	}
 
 	if (gprs->contexts == NULL)
-		add_context(gprs, "Internet", GPRS_CONTEXT_TYPE_INTERNET);
+		add_context(gprs, NULL, OFONO_GPRS_CONTEXT_TYPE_INTERNET);
 
 	gprs->netreg_watch = __ofono_modem_add_atom_watch(modem,
 					OFONO_ATOM_TYPE_NETREG,
--- src/history.c
+++ src/history.c
@@ -255,7 +255,7 @@
 {
 	DBG("driver: %p name: %s", driver, driver->name);
 
-	history_drivers = g_slist_prepend(history_drivers, (void *)driver);
+	history_drivers = g_slist_prepend(history_drivers, (void *) driver);
 
 	return 0;
 }
--- src/main.c
+++ src/main.c
@@ -138,6 +138,11 @@
 
 #ifdef HAVE_CAPNG
 	/* Drop capabilities */
+	capng_clear(CAPNG_SELECT_BOTH);
+	capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+				CAP_NET_BIND_SERVICE, CAP_NET_ADMIN,
+				CAP_NET_RAW, CAP_SYS_ADMIN, -1);
+	capng_apply(CAPNG_SELECT_BOTH);
 #endif
 
 	sigemptyset(&mask);
--- src/manager.c
+++ src/manager.c
@@ -35,6 +35,9 @@
 	const char *path = ofono_modem_get_path(modem);
 	DBusMessageIter entry, dict;
 
+	if (ofono_modem_is_registered(modem) == FALSE)
+		return;
+
 	dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
 						NULL, &entry);
 	dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
--- src/modem.c
+++ src/modem.c
@@ -72,6 +72,7 @@
 	ofono_bool_t		powered_pending;
 	guint			timeout;
 	ofono_bool_t		online;
+	struct ofono_watchlist	*online_watches;
 	GHashTable		*properties;
 	struct ofono_sim	*sim;
 	unsigned int		sim_watch;
@@ -362,6 +363,22 @@
 	}
 }
 
+static void notify_online_watches(struct ofono_modem *modem)
+{
+	struct ofono_watchlist_item *item;
+	GSList *l;
+	ofono_modem_online_notify_func notify;
+
+	if (modem->online_watches == NULL)
+		return;
+
+	for (l = modem->online_watches->items; l; l = l->next) {
+		item = l->data;
+		notify = item->notify;
+		notify(modem->online, item->notify_data);
+	}
+}
+
 static void modem_change_state(struct ofono_modem *modem,
 				enum modem_state new_state)
 {
@@ -403,12 +420,16 @@
 				driver->post_sim(modem);
 			__ofono_history_probe_drivers(modem);
 			__ofono_nettime_probe_drivers(modem);
-		}
+		} else
+			notify_online_watches(modem);
+
 		break;
 
 	case MODEM_STATE_ONLINE:
 		if (driver->post_online)
 			driver->post_online(modem);
+
+		notify_online_watches(modem);
 		break;
 	}
 }
@@ -437,6 +458,30 @@
 	}
 }
 
+unsigned int __ofono_modem_add_online_watch(struct ofono_modem *modem,
+					ofono_modem_online_notify_func notify,
+					void *data, ofono_destroy_func destroy)
+{
+	struct ofono_watchlist_item *item;
+
+	if (modem == NULL || notify == NULL)
+		return 0;
+
+	item = g_new0(struct ofono_watchlist_item, 1);
+
+	item->notify = notify;
+	item->destroy = destroy;
+	item->notify_data = data;
+
+	return __ofono_watchlist_add_item(modem->online_watches, item);
+}
+
+void __ofono_modem_remove_online_watch(struct ofono_modem *modem,
+					unsigned int id)
+{
+	__ofono_watchlist_remove_item(modem->online_watches, id);
+}
+
 static void online_cb(const struct ofono_error *error, void *data)
 {
 	struct ofono_modem *modem = data;
@@ -468,7 +513,7 @@
 	__ofono_dbus_pending_reply(&modem->pending, reply);
 
 	if (error->type == OFONO_ERROR_TYPE_NO_ERROR &&
-		modem->modem_state == MODEM_STATE_ONLINE)
+				modem->modem_state == MODEM_STATE_ONLINE)
 		modem_change_state(modem, MODEM_STATE_OFFLINE);
 }
 
@@ -752,6 +797,7 @@
 void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered)
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
+	dbus_bool_t dbus_powered = powered;
 
 	if (modem->timeout > 0) {
 		g_source_remove(modem->timeout);
@@ -771,33 +817,34 @@
 
 	modem->powered_pending = powered;
 
-	if (modem->powered != powered) {
-		dbus_bool_t dbus_powered = powered;
-		modem->powered = powered;
+	if (modem->powered == powered)
+		goto out;
 
-		if (modem->driver == NULL) {
-			ofono_error("Calling ofono_modem_set_powered on a"
-					"modem with no driver is not valid, "
-					"please fix the modem driver.");
-			return;
-		}
+	modem->powered = powered;
 
-		ofono_dbus_signal_property_changed(conn, modem->path,
-						OFONO_MODEM_INTERFACE,
-						"Powered", DBUS_TYPE_BOOLEAN,
-						&dbus_powered);
+	if (modem->driver == NULL) {
+		ofono_error("Calling ofono_modem_set_powered on a"
+				"modem with no driver is not valid, "
+				"please fix the modem driver.");
+		return;
+	}
 
-		if (powered) {
-			modem_change_state(modem, MODEM_STATE_PRE_SIM);
+	ofono_dbus_signal_property_changed(conn, modem->path,
+					OFONO_MODEM_INTERFACE,
+					"Powered", DBUS_TYPE_BOOLEAN,
+					&dbus_powered);
 
-			/* Force SIM Ready for devies with no sim atom */
-			if (__ofono_modem_find_atom(modem,
-						OFONO_ATOM_TYPE_SIM) == NULL)
-				sim_state_watch(OFONO_SIM_STATE_READY, modem);
-		} else
-			modem_change_state(modem, MODEM_STATE_POWER_OFF);
-	}
+	if (powered) {
+		modem_change_state(modem, MODEM_STATE_PRE_SIM);
+
+		/* Force SIM Ready for devices with no sim atom */
+		if (__ofono_modem_find_atom(modem,
+					OFONO_ATOM_TYPE_SIM) == NULL)
+			sim_state_watch(OFONO_SIM_STATE_READY, modem);
+	} else
+		modem_change_state(modem, MODEM_STATE_POWER_OFF);
 
+out:
 	if (powering_down && powered == FALSE) {
 		modems_remaining -= 1;
 
@@ -1057,7 +1104,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_devinfo_drivers = g_slist_prepend(g_devinfo_drivers, (void *)d);
+	g_devinfo_drivers = g_slist_prepend(g_devinfo_drivers, (void *) d);
 
 	return 0;
 }
@@ -1066,7 +1113,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_devinfo_drivers = g_slist_remove(g_devinfo_drivers, (void *)d);
+	g_devinfo_drivers = g_slist_remove(g_devinfo_drivers, (void *) d);
 }
 
 static void devinfo_remove(struct ofono_atom *atom)
@@ -1413,6 +1460,17 @@
 	g_dbus_send_message(ofono_dbus_get_connection(), signal);
 }
 
+ofono_bool_t ofono_modem_is_registered(struct ofono_modem *modem)
+{
+	if (modem == NULL)
+		return FALSE;
+
+	if (modem->driver == NULL)
+		return FALSE;
+
+	return TRUE;
+}
+
 int ofono_modem_register(struct ofono_modem *modem)
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
@@ -1461,6 +1519,7 @@
 	modem->driver_type = NULL;
 
 	modem->atom_watches = __ofono_watchlist_new(g_free);
+	modem->online_watches = __ofono_watchlist_new(g_free);
 
 	emit_modem_added(modem);
 	call_modemwatches(modem, TRUE);
@@ -1492,6 +1551,9 @@
 	__ofono_watchlist_free(modem->atom_watches);
 	modem->atom_watches = NULL;
 
+	__ofono_watchlist_free(modem->online_watches);
+	modem->online_watches = NULL;
+
 	modem->sim_watch = 0;
 	modem->sim_ready_watch = 0;
 
@@ -1559,7 +1621,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_driver_list = g_slist_prepend(g_driver_list, (void *)d);
+	g_driver_list = g_slist_prepend(g_driver_list, (void *) d);
 
 	return 0;
 }
@@ -1571,7 +1633,7 @@
 
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_driver_list = g_slist_remove(g_driver_list, (void *)d);
+	g_driver_list = g_slist_remove(g_driver_list, (void *) d);
 
 	for (l = g_modem_list; l; l = l->next) {
 		modem = l->data;
--- src/nettime.c
+++ src/nettime.c
@@ -108,7 +108,7 @@
 {
 	DBG("driver: %p name: %s", driver, driver->name);
 
-	nettime_drivers = g_slist_prepend(nettime_drivers, (void *)driver);
+	nettime_drivers = g_slist_prepend(nettime_drivers, (void *) driver);
 
 	return 0;
 }
--- src/network.c
+++ src/network.c
@@ -1646,7 +1646,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -1655,7 +1655,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void netreg_unregister(struct ofono_atom *atom)
--- src/ofono.conf
+++ src/ofono.conf
@@ -11,6 +11,8 @@
     <allow own="org.ofono"/>
     <allow send_destination="org.ofono"/>
     <allow send_interface="org.ofono.SimToolkitAgent"/>
+    <allow send_interface="org.ofono.PushNotificationAgent"/>
+    <allow send_interface="org.ofono.SmartMessagingAgent"/>
   </policy>
 
   <policy at_console="true">
--- src/ofono.h
+++ src/ofono.h
@@ -57,6 +57,7 @@
 DBusMessage *__ofono_error_not_attached(DBusMessage *msg);
 DBusMessage *__ofono_error_attach_in_progress(DBusMessage *msg);
 DBusMessage *__ofono_error_canceled(DBusMessage *msg);
+DBusMessage *__ofono_error_access_denied(DBusMessage *msg);
 
 void __ofono_dbus_pending_reply(DBusMessage **msg, DBusMessage *reply);
 
@@ -177,6 +178,13 @@
 					ofono_destroy_func destroy);
 gboolean __ofono_modemwatch_remove(unsigned int id);
 
+typedef void (*ofono_modem_online_notify_func)(ofono_bool_t online, void *data);
+unsigned int __ofono_modem_add_online_watch(struct ofono_modem *modem,
+					ofono_modem_online_notify_func notify,
+					void *data, ofono_destroy_func destroy);
+void __ofono_modem_remove_online_watch(struct ofono_modem *modem,
+					unsigned int id);
+
 #include <ofono/call-barring.h>
 
 gboolean __ofono_call_barring_is_busy(struct ofono_call_barring *cb);
@@ -232,9 +240,13 @@
 	OFONO_SMS_SUBMIT_FLAG_REQUEST_SR =	0x1,
 	OFONO_SMS_SUBMIT_FLAG_RECORD_HISTORY =	0x2,
 	OFONO_SMS_SUBMIT_FLAG_RETRY =		0x4,
+	OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS =	0x8,
 };
 
 typedef void (*ofono_sms_txq_submit_cb_t)(gboolean ok, void *data);
+typedef void (*ofono_sms_txq_queued_cb_t)(struct ofono_sms *sms,
+						const struct ofono_uuid *uuid,
+						void *data);
 typedef void (*ofono_sms_text_notify_cb_t)(const char *from,
 						const struct tm *remote,
 						const struct tm *local,
@@ -250,8 +262,16 @@
 
 int __ofono_sms_txq_submit(struct ofono_sms *sms, GSList *list,
 				unsigned int flags, struct ofono_uuid *uuid,
-				ofono_sms_txq_submit_cb_t cb,
-				void *data, ofono_destroy_func destroy);
+				ofono_sms_txq_queued_cb_t, void *data);
+
+int __ofono_sms_txq_set_submit_notify(struct ofono_sms *sms,
+					struct ofono_uuid *uuid,
+					ofono_sms_txq_submit_cb_t cb,
+					void *data,
+					ofono_destroy_func destroy);
+
+const char *__ofono_sms_message_path_from_uuid(struct ofono_sms *sms,
+						const struct ofono_uuid *uuid);
 
 unsigned int __ofono_sms_text_watch_add(struct ofono_sms *sms,
 					ofono_sms_text_notify_cb_t cb,
@@ -266,12 +286,27 @@
 gboolean __ofono_sms_datagram_watch_remove(struct ofono_sms *sms,
 					unsigned int id);
 
+unsigned short __ofono_sms_get_next_ref(struct ofono_sms *sms);
+
 #include <ofono/sim.h>
+
+ofono_bool_t __ofono_sim_service_available(struct ofono_sim *sim,
+						int ust_service,
+						int sst_service);
+
 #include <ofono/stk.h>
 
+typedef void (*__ofono_sms_sim_download_cb_t)(ofono_bool_t ok,
+						const unsigned char *tp_ud,
+						int len, void *data);
+
 struct cbs;
 void __ofono_cbs_sim_download(struct ofono_stk *stk, const struct cbs *msg);
 
+struct sms;
+int __ofono_sms_sim_download(struct ofono_stk *stk, const struct sms *msg,
+				__ofono_sms_sim_download_cb_t cb, void *data);
+
 #include <ofono/ssn.h>
 
 typedef void (*ofono_ssn_mo_notify_cb)(int index, void *user);
--- src/phonebook.c
+++ src/phonebook.c
@@ -249,7 +249,7 @@
 	vcard_printf_begin(vcards);
 	vcard_printf_text(vcards, person->text);
 
-	g_slist_foreach(person->number_list, (GFunc)print_number, vcards);
+	g_slist_foreach(person->number_list, (GFunc) print_number, vcards);
 
 	vcard_printf_group(vcards, person->group);
 	vcard_printf_email(vcards, person->email);
@@ -264,7 +264,7 @@
 	g_free(person->email);
 	g_free(person->sip_uri);
 
-	g_slist_foreach(person->number_list, (GFunc)destroy_number, NULL);
+	g_slist_foreach(person->number_list, (GFunc) destroy_number, NULL);
 	g_slist_free(person->number_list);
 
 	g_free(person);
@@ -420,9 +420,9 @@
 
 	/* convert the collected entries that are already merged to vcard */
 	phonebook->merge_list = g_slist_reverse(phonebook->merge_list);
-	g_slist_foreach(phonebook->merge_list, (GFunc)print_merged_entry,
+	g_slist_foreach(phonebook->merge_list, (GFunc) print_merged_entry,
 				phonebook->vcards);
-	g_slist_foreach(phonebook->merge_list, (GFunc)destroy_merged_entry,
+	g_slist_foreach(phonebook->merge_list, (GFunc) destroy_merged_entry,
 				NULL);
 	g_slist_free(phonebook->merge_list);
 	phonebook->merge_list = NULL;
@@ -498,7 +498,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -507,7 +507,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void phonebook_unregister(struct ofono_atom *atom)
--- src/plugin.c
+++ src/plugin.c
@@ -176,6 +176,9 @@
 		plugin->active = TRUE;
 	}
 
+	g_strfreev(patterns);
+	g_strfreev(excludes);
+
 	return 0;
 }
 
--- src/radio-settings.c
+++ src/radio-settings.c
@@ -33,7 +33,7 @@
 #include "ofono.h"
 #include "common.h"
 
-#define RADIO_SETTINGS_MODE_CACHED 0x1
+#define RADIO_SETTINGS_FLAG_CACHED 0x1
 
 static GSList *g_drivers = NULL;
 
@@ -42,6 +42,8 @@
 	int flags;
 	enum ofono_radio_access_mode mode;
 	enum ofono_radio_access_mode pending_mode;
+	ofono_bool_t fast_dormancy;
+	ofono_bool_t fast_dormancy_pending;
 	const struct ofono_radio_settings_driver *driver;
 	void *driver_data;
 	struct ofono_atom *atom;
@@ -106,11 +108,57 @@
 	ofono_dbus_dict_append(&dict, "TechnologyPreference",
 					DBUS_TYPE_STRING, &mode);
 
+	if (rs->driver->query_fast_dormancy) {
+		dbus_bool_t value = rs->fast_dormancy;
+		ofono_dbus_dict_append(&dict, "FastDormancy",
+					DBUS_TYPE_BOOLEAN, &value);
+	}
+
 	dbus_message_iter_close_container(&iter, &dict);
 
 	return reply;
 }
 
+static void radio_set_fast_dormancy(struct ofono_radio_settings *rs,
+					ofono_bool_t enable)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = __ofono_atom_get_path(rs->atom);
+	dbus_bool_t value = enable;
+
+	if (rs->fast_dormancy == enable)
+		return;
+
+	ofono_dbus_signal_property_changed(conn, path,
+						OFONO_RADIO_SETTINGS_INTERFACE,
+						"FastDormancy",
+						DBUS_TYPE_BOOLEAN, &value);
+	rs->fast_dormancy = enable;
+}
+
+static void radio_fast_dormancy_set_callback(const struct ofono_error *error,
+						void *data)
+{
+	struct ofono_radio_settings *rs = data;
+	DBusMessage *reply;
+
+	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+		DBG("Error setting fast dormancy");
+
+		rs->fast_dormancy_pending = rs->fast_dormancy;
+
+		reply = __ofono_error_failed(rs->pending);
+		__ofono_dbus_pending_reply(&rs->pending, reply);
+
+		return;
+	}
+
+	reply = dbus_message_new_method_return(rs->pending);
+	__ofono_dbus_pending_reply(&rs->pending, reply);
+
+	radio_set_fast_dormancy(rs, rs->fast_dormancy_pending);
+}
+
 static void radio_set_rat_mode(struct ofono_radio_settings *rs,
 				enum ofono_radio_access_mode mode)
 {
@@ -122,7 +170,6 @@
 		return;
 
 	rs->mode = mode;
-	rs->flags |= RADIO_SETTINGS_MODE_CACHED;
 
 	path = __ofono_atom_get_path(rs->atom);
 	str_mode = radio_access_mode_to_string(rs->mode);
@@ -140,9 +187,12 @@
 
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		DBG("Error setting radio access mode");
+
 		rs->pending_mode = rs->mode;
+
 		reply = __ofono_error_failed(rs->pending);
 		__ofono_dbus_pending_reply(&rs->pending, reply);
+
 		return;
 	}
 
@@ -152,6 +202,46 @@
 	radio_set_rat_mode(rs, rs->pending_mode);
 }
 
+static void radio_send_properties_reply(struct ofono_radio_settings *rs)
+{
+	DBusMessage *reply;
+
+	rs->flags |= RADIO_SETTINGS_FLAG_CACHED;
+
+	reply = radio_get_properties_reply(rs->pending, rs);
+	__ofono_dbus_pending_reply(&rs->pending, reply);
+}
+
+static void radio_fast_dormancy_query_callback(const struct ofono_error *error,
+						ofono_bool_t enable, void *data)
+{
+	struct ofono_radio_settings *rs = data;
+	DBusMessage *reply;
+
+	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+		DBG("Error during fast dormancy query");
+
+		reply = __ofono_error_failed(rs->pending);
+		__ofono_dbus_pending_reply(&rs->pending, reply);
+
+		return;
+	}
+
+	radio_set_fast_dormancy(rs, enable);
+	radio_send_properties_reply(rs);
+}
+
+static void radio_query_fast_dormancy(struct ofono_radio_settings *rs)
+{
+	if (!rs->driver->query_fast_dormancy) {
+		radio_send_properties_reply(rs);
+		return;
+	}
+
+	rs->driver->query_fast_dormancy(rs, radio_fast_dormancy_query_callback,
+					rs);
+}
+
 static void radio_rat_mode_query_callback(const struct ofono_error *error,
 					enum ofono_radio_access_mode mode,
 					void *data)
@@ -161,15 +251,15 @@
 
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		DBG("Error during radio access mode query");
+
 		reply = __ofono_error_failed(rs->pending);
 		__ofono_dbus_pending_reply(&rs->pending, reply);
+
 		return;
 	}
 
 	radio_set_rat_mode(rs, mode);
-
-	reply = radio_get_properties_reply(rs->pending, rs);
-	__ofono_dbus_pending_reply(&rs->pending, reply);
+	radio_query_fast_dormancy(rs);
 }
 
 static DBusMessage *radio_get_properties(DBusConnection *conn,
@@ -177,7 +267,7 @@
 {
 	struct ofono_radio_settings *rs = data;
 
-	if (rs->flags & RADIO_SETTINGS_MODE_CACHED)
+	if (rs->flags & RADIO_SETTINGS_FLAG_CACHED)
 		return radio_get_properties_reply(msg, rs);
 
 	if (!rs->driver->query_rat_mode)
@@ -240,6 +330,28 @@
 		rs->driver->set_rat_mode(rs, mode, radio_mode_set_callback, rs);
 
 		return NULL;
+	} else if (g_strcmp0(property, "FastDormancy") == 0) {
+		dbus_bool_t value;
+		int target;
+
+		if (!rs->driver->set_fast_dormancy)
+			return __ofono_error_not_implemented(msg);
+
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+			return __ofono_error_invalid_args(msg);
+
+		dbus_message_iter_get_basic(&var, &value);
+		target = value;
+
+		if (rs->fast_dormancy_pending == target)
+			return dbus_message_new_method_return(msg);
+
+		rs->pending = dbus_message_ref(msg);
+		rs->fast_dormancy_pending = target;
+
+		rs->driver->set_fast_dormancy(rs, target,
+					radio_fast_dormancy_set_callback, rs);
+		return NULL;
 	}
 
 	return __ofono_error_invalid_args(msg);
@@ -265,7 +377,7 @@
 	if (!d || !d->probe)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -277,7 +389,7 @@
 	if (!d)
 		return;
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void radio_settings_unregister(struct ofono_atom *atom)
--- src/sim.c
+++ src/sim.c
@@ -73,6 +73,7 @@
 	unsigned char *efsst;
 	unsigned char efsst_length;
 	gboolean fixed_dialing;
+	gboolean barred_dialing;
 
 	char *imsi;
 
@@ -288,6 +289,7 @@
 	const char *pin_name;
 	dbus_bool_t present = sim->state != OFONO_SIM_STATE_NOT_PRESENT;
 	dbus_bool_t fdn;
+	dbus_bool_t bdn;
 
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
@@ -315,6 +317,9 @@
 	fdn = sim->fixed_dialing;
 	ofono_dbus_dict_append(&dict, "FixedDialing", DBUS_TYPE_BOOLEAN, &fdn);
 
+	bdn = sim->barred_dialing;
+	ofono_dbus_dict_append(&dict, "BarredDialing", DBUS_TYPE_BOOLEAN, &bdn);
+
 	if (sim->mnc_length && sim->imsi) {
 		char mcc[OFONO_MAX_MCC_LENGTH + 1];
 		char mnc[OFONO_MAX_MNC_LENGTH + 1];
@@ -1261,6 +1266,58 @@
 						DBUS_TYPE_BOOLEAN, &val);
 }
 
+static void sim_bdn_enabled(struct ofono_sim *sim)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = __ofono_atom_get_path(sim->atom);
+	dbus_bool_t val;
+
+	sim->barred_dialing = TRUE;
+
+	val = sim->barred_dialing;
+	ofono_dbus_signal_property_changed(conn, path,
+						OFONO_SIM_MANAGER_INTERFACE,
+						"BarredDialing",
+						DBUS_TYPE_BOOLEAN, &val);
+}
+
+static void sim_efbdn_info_read_cb(int ok, unsigned char file_status,
+					int total_length, int record_length,
+					void *userdata)
+{
+	struct ofono_sim *sim = userdata;
+
+	if (!ok)
+		goto out;
+
+	if (file_status & SIM_FILE_STATUS_VALID)
+		sim_bdn_enabled(sim);
+
+out:
+	if (sim->fixed_dialing != TRUE &&
+			sim->barred_dialing != TRUE)
+		sim_retrieve_imsi(sim);
+}
+
+static gboolean check_bdn_status(struct ofono_sim *sim)
+{
+	/*
+	 * Check the status of Barred Dialing in the SIM-card
+	 * (TS 11.11/TS 51.011, Section 11.5.1: BDN capability request).
+	 * If BDN is allocated, activated in EFsst and EFbdn is validated,
+	 * halt the SIM initialization.
+	 */
+	if (sim_sst_is_active(sim->efsst, sim->efsst_length,
+			SIM_SST_SERVICE_BDN)) {
+		sim_fs_read_info(sim->simfs, SIM_EFBDN_FILEID,
+				OFONO_SIM_FILE_STRUCTURE_FIXED,
+				sim_efbdn_info_read_cb, sim);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
 static void sim_efadn_info_read_cb(int ok, unsigned char file_status,
 					int total_length, int record_length,
 					void *userdata)
@@ -1270,13 +1327,15 @@
 	if (!ok)
 		goto out;
 
-	if (file_status != SIM_FILE_STATUS_VALID) {
+	if (!(file_status & SIM_FILE_STATUS_VALID))
 		sim_fdn_enabled(sim);
-		return;
-	}
 
 out:
-	sim_retrieve_imsi(sim);
+	if (check_bdn_status(sim) != TRUE) {
+		if (sim->fixed_dialing != TRUE &&
+				sim->barred_dialing != TRUE)
+			sim_retrieve_imsi(sim);
+	}
 }
 
 static void sim_efsst_read_cb(int ok, int length, int record,
@@ -1310,6 +1369,9 @@
 		return;
 	}
 
+	if (check_bdn_status(sim) == TRUE)
+		return;
+
 out:
 	sim_retrieve_imsi(sim);
 }
@@ -1319,6 +1381,7 @@
 				int record_length, void *userdata)
 {
 	struct ofono_sim *sim = userdata;
+	gboolean available;
 
 	if (!ok)
 		goto out;
@@ -1336,14 +1399,27 @@
 	 * (TS 31.102, Section 5.3.2: FDN capability request).
 	 * If FDN is activated, don't continue initialization routine.
 	 */
-	if (sim_est_is_active(sim->efest, sim->efest_length,
-				SIM_EST_SERVICE_FDN)) {
+	available = sim_ust_is_available(sim->efust, sim->efust_length,
+						SIM_UST_SERVICE_FDN);
+	if (available && sim_est_is_active(sim->efest, sim->efest_length,
+						SIM_EST_SERVICE_FDN))
 		sim_fdn_enabled(sim);
-		return;
-	}
+
+	/*
+	 * Check the status of Barred Dialing in the USIM-card
+	 * (TS 31.102, Section 5.3.2: BDN capability request).
+	 * If BDN service is enabled, halt the USIM initialization.
+	 */
+	available = sim_ust_is_available(sim->efust, sim->efust_length,
+						SIM_UST_SERVICE_BDN);
+	if (available && sim_est_is_active(sim->efest, sim->efest_length,
+						SIM_EST_SERVICE_BDN))
+		sim_bdn_enabled(sim);
 
 out:
-	sim_retrieve_imsi(sim);
+	if (sim->fixed_dialing != TRUE &&
+			sim->barred_dialing != TRUE)
+		sim_retrieve_imsi(sim);
 }
 
 static void sim_efust_read_cb(int ok, int length, int record,
@@ -1363,11 +1439,19 @@
 	sim->efust = g_memdup(data, length);
 	sim->efust_length = length;
 
-	ofono_sim_read(sim, SIM_EFEST_FILEID,
-			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
-			sim_efest_read_cb, sim);
+	/*
+	 * Check whether the SIM provides EFest file
+	 * According to 31.102, section 4.2.24 and 4.2.44 the EFest file
+	 * must be present if EFfdn or EFbdn are present
+	 */
+	if (sim_ust_is_available(sim->efust, sim->efust_length,
+				SIM_UST_SERVICE_ENABLED_SERVICE_TABLE)) {
+		ofono_sim_read(sim, SIM_EFEST_FILEID,
+				OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+				sim_efest_read_cb, sim);
 
-	return;
+		return;
+	}
 
 out:
 	sim_retrieve_imsi(sim);
@@ -1844,6 +1928,21 @@
 	return sim->cphs_service_table;
 }
 
+ofono_bool_t __ofono_sim_service_available(struct ofono_sim *sim,
+						int ust_service,
+						int sst_service)
+{
+	if (sim->efust)
+		return sim_ust_is_available(sim->efust, sim->efust_length,
+						ust_service);
+
+	if (sim->efsst)
+		return sim_sst_is_active(sim->efsst, sim->efsst_length,
+						sst_service);
+
+	return FALSE;
+}
+
 static void sim_inserted_update(struct ofono_sim *sim)
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
@@ -1922,6 +2021,7 @@
 	sim->iidf_image = NULL;
 
 	sim->fixed_dialing = FALSE;
+	sim->barred_dialing = FALSE;
 }
 
 void ofono_sim_inserted_notify(struct ofono_sim *sim, ofono_bool_t inserted)
@@ -2020,7 +2120,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -2029,7 +2129,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void sim_unregister(struct ofono_atom *atom)
--- src/simfs.c
+++ src/simfs.c
@@ -98,7 +98,7 @@
 	 * for operations still in progress
 	 */
 	if (fs->op_q) {
-		g_queue_foreach(fs->op_q, (GFunc)sim_fs_op_free, NULL);
+		g_queue_foreach(fs->op_q, (GFunc) sim_fs_op_free, NULL);
 		g_queue_free(fs->op_q);
 	}
 
@@ -142,7 +142,10 @@
 {
 	struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
 
-	if (op->is_read == TRUE)
+	if (op->info_only == TRUE)
+		((sim_fs_read_info_cb_t) op->cb)
+			(0, 0, 0, 0, op->userdata);
+	else if (op->is_read == TRUE)
 		((ofono_sim_file_read_cb_t) op->cb)
 			(0, 0, 0, 0, 0, op->userdata);
 	else
--- src/simutil.c
+++ src/simutil.c
@@ -813,7 +813,7 @@
 			if (buffer[i] == 0xff && buffer[i + 1] == 0xff)
 				break;
 
-		ret = g_convert((const char *)buffer, length,
+		ret = g_convert((const char *) buffer, length,
 					"UTF-8//TRANSLIT", "UCS-2BE",
 					NULL, NULL, NULL);
 		break;
--- src/simutil.h
+++ src/simutil.h
@@ -32,6 +32,7 @@
 	SIM_EFMSISDN_FILEID = 0x6f40,
 	SIM_EFSPN_FILEID = 0x6f46,
 	SIM_EFSDN_FILEID = 0x6f49,
+	SIM_EFBDN_FILEID = 0x6f4d,
 	SIM_EFADN_FILEID = 0x6f3a,
 	SIM_EFEST_FILEID = 0x6f56,
 	SIM_EFAD_FILEID = 0x6fad,
--- src/sms.c
+++ src/sms.c
@@ -37,6 +37,7 @@
 #include "util.h"
 #include "smsutil.h"
 #include "storage.h"
+#include "simutil.h"
 
 #define uninitialized_var(x) x = x
 
@@ -60,6 +61,7 @@
 struct message {
 	struct ofono_uuid uuid;
 	enum message_state state;
+	struct tx_queue_entry *entry;
 };
 
 struct sms_handler {
@@ -203,7 +205,7 @@
 	handler->item.destroy = destroy;
 
 	return __ofono_watchlist_add_item(watchlist,
-				(struct ofono_watchlist_item *)handler);
+				(struct ofono_watchlist_item *) handler);
 }
 
 unsigned int __ofono_sms_text_watch_add(struct ofono_sms *sms,
@@ -311,14 +313,14 @@
 	g_free(m);
 }
 
-static const char *message_build_path(struct ofono_sms *sms,
-						struct message *m)
+const char *__ofono_sms_message_path_from_uuid(struct ofono_sms *sms,
+						const struct ofono_uuid *uuid)
 {
 	static char path[256];
 
 	snprintf(path, sizeof(path), "%s/message_%s",
 			__ofono_atom_get_path(sms->atom),
-			ofono_uuid_to_str(&m->uuid));
+			ofono_uuid_to_str(uuid));
 
 	return path;
 }
@@ -331,7 +333,7 @@
 	if (!m)
 		return FALSE;
 
-	path = message_build_path(sms, m);
+	path = __ofono_sms_message_path_from_uuid(sms, &m->uuid);
 
 	if (!g_dbus_register_interface(conn, path, OFONO_MESSAGE_INTERFACE,
 					message_methods, message_signals,
@@ -349,7 +351,7 @@
 						struct message *m)
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
-	const char *path = message_build_path(sms, m);
+	const char *path = __ofono_sms_message_path_from_uuid(sms, &m->uuid);
 
 	return g_dbus_unregister_interface(conn, path,
 						OFONO_MESSAGE_INTERFACE);
@@ -373,7 +375,7 @@
 
 	dbus_message_iter_init_append(signal, &iter);
 
-	path = message_build_path(sms, m);
+	path = __ofono_sms_message_path_from_uuid(sms, &m->uuid);
 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
 
 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
@@ -389,7 +391,7 @@
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
 	const char *atompath = __ofono_atom_get_path(sms->atom);
-	const char *path = message_build_path(sms, m);
+	const char *path = __ofono_sms_message_path_from_uuid(sms, &m->uuid);
 
 	g_dbus_emit_signal(conn, atompath, OFONO_MESSAGE_MANAGER_INTERFACE,
 				"MessageRemoved", DBUS_TYPE_OBJECT_PATH, &path,
@@ -414,7 +416,7 @@
 		return;
 
 	m->state = new_state;
-	path = message_build_path(sms, m);
+	path = __ofono_sms_message_path_from_uuid(sms, uuid);
 	state = message_state_to_string(m->state);
 
 	ofono_dbus_signal_property_changed(conn, path,
@@ -424,6 +426,8 @@
 
 	if (m->state == MESSAGE_STATE_SENT ||
 			m->state == MESSAGE_STATE_FAILED) {
+		m->entry = NULL;
+
 		g_hash_table_remove(sms->messages, uuid);
 		emit_message_removed(sms, m);
 		message_dbus_unregister(sms, m);
@@ -785,18 +789,24 @@
 
 	if (entry->flags & OFONO_SMS_SUBMIT_FLAG_RECORD_HISTORY) {
 		enum ofono_history_sms_status hs;
-		enum message_state ms;
 
-		if (ok) {
+		if (ok)
 			hs = OFONO_HISTORY_SMS_STATUS_SUBMITTED;
-			ms = MESSAGE_STATE_SENT;
-		} else {
+		else
 			hs = OFONO_HISTORY_SMS_STATUS_SUBMIT_FAILED;
-			ms = MESSAGE_STATE_FAILED;
-		}
 
 		__ofono_history_sms_send_status(modem, &entry->uuid,
 						time(NULL), hs);
+	}
+
+	if (entry->flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS) {
+		enum message_state ms;
+
+		if (ok)
+			ms = MESSAGE_STATE_SENT;
+		else
+			ms = MESSAGE_STATE_FAILED;
+
 		message_set_state(sms, &entry->uuid, ms);
 	}
 
@@ -835,29 +845,6 @@
 	return FALSE;
 }
 
-static void set_ref_and_to(GSList *msg_list, guint16 ref, int offset,
-				gboolean use_16bit, const char *to)
-{
-	GSList *l;
-	struct sms *sms;
-
-	for (l = msg_list; l; l = l->next) {
-		sms = l->data;
-
-		sms_address_from_string(&sms->submit.daddr, to);
-
-		if (offset == 0)
-			continue;
-
-		if (use_16bit) {
-			sms->submit.ud[offset] = (ref & 0xf0) >> 8;
-			sms->submit.ud[offset+1] = ref & 0x0f;
-		} else {
-			sms->submit.ud[offset] = ref & 0x0f;
-		}
-	}
-}
-
 /**
  * Generate a UUID from an SMS PDU List
  *
@@ -900,10 +887,7 @@
 }
 
 static struct tx_queue_entry *tx_queue_entry_new(GSList *msg_list,
-						unsigned int flags,
-						ofono_sms_txq_submit_cb_t cb,
-						void *data,
-						ofono_destroy_func destroy)
+							unsigned int flags)
 {
 	struct tx_queue_entry *entry;
 	int i = 0;
@@ -927,9 +911,6 @@
 	}
 
 	entry->flags = flags;
-	entry->cb = cb;
-	entry->data = data;
-	entry->destroy = destroy;
 
 	for (l = msg_list; l; l = l->next) {
 		struct pending_pdu *pdu = &entry->pdus[i++];
@@ -951,6 +932,28 @@
 	return NULL;
 }
 
+static void tx_queue_entry_set_submit_notify(struct tx_queue_entry *entry,
+						ofono_sms_txq_submit_cb_t cb,
+						void *data,
+						ofono_destroy_func destroy)
+{
+	entry->cb = cb;
+	entry->data = data;
+	entry->destroy = destroy;
+}
+
+static void message_queued(struct ofono_sms *sms,
+				const struct ofono_uuid *uuid, void *data)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	DBusMessage *msg = data;
+	const char *path;
+
+	path = __ofono_sms_message_path_from_uuid(sms, uuid);
+	g_dbus_send_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_INVALID);
+}
+
 /*
  * Pre-process a SMS text message and deliver it [D-Bus SendMessage()]
  *
@@ -971,13 +974,11 @@
 	const char *to;
 	const char *text;
 	GSList *msg_list;
-	int ref_offset;
 	struct ofono_modem *modem;
 	unsigned int flags;
 	gboolean use_16bit_ref = FALSE;
-	struct tx_queue_entry *entry;
-	struct message *m;
-	const char *path;
+	int err;
+	struct ofono_uuid uuid;
 
 	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &to,
 					DBUS_TYPE_STRING, &text,
@@ -987,63 +988,31 @@
 	if (valid_phone_number_format(to) == FALSE)
 		return __ofono_error_invalid_format(msg);
 
-	msg_list = sms_text_prepare(text, 0, use_16bit_ref, &ref_offset,
-					sms->use_delivery_reports);
+	msg_list = sms_text_prepare(to, text, sms->ref, use_16bit_ref,
+						sms->use_delivery_reports);
 
 	if (!msg_list)
 		return __ofono_error_invalid_format(msg);
 
-	set_ref_and_to(msg_list, sms->ref, ref_offset, use_16bit_ref, to);
-	DBG("ref: %d, offset: %d", sms->ref, ref_offset);
-
-	if (ref_offset != 0) {
-		if (sms->ref == 65536)
-			sms->ref = 1;
-		else
-			sms->ref = sms->ref + 1;
-	}
-
 	flags = OFONO_SMS_SUBMIT_FLAG_RECORD_HISTORY;
 	flags |= OFONO_SMS_SUBMIT_FLAG_RETRY;
+	flags |= OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS;
 	if (sms->use_delivery_reports)
 		flags |= OFONO_SMS_SUBMIT_FLAG_REQUEST_SR;
 
-	entry = tx_queue_entry_new(msg_list, flags, NULL, NULL, NULL);
+	err = __ofono_sms_txq_submit(sms, msg_list, flags, &uuid,
+					message_queued, msg);
 
-	g_slist_foreach(msg_list, (GFunc)g_free, NULL);
+	g_slist_foreach(msg_list, (GFunc) g_free, NULL);
 	g_slist_free(msg_list);
 
-	if (entry == NULL)
-		goto err;
-
-	m = message_create(&entry->uuid);
-	if (m == NULL)
-		goto err;
-
-	if (message_dbus_register(sms, m) == FALSE)
-		goto err;
-
-	g_hash_table_insert(sms->messages, &m->uuid, m);
-
-	g_queue_push_tail(sms->txq, entry);
-
-	if (g_queue_get_length(sms->txq) == 1)
-		sms->tx_source = g_timeout_add(0, tx_next, sms);
-
-	path = message_build_path(sms, m);
-	g_dbus_send_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &path,
-					DBUS_TYPE_INVALID);
+	if (err < 0)
+		return __ofono_error_failed(msg);
 
 	modem = __ofono_atom_get_modem(sms->atom);
-	__ofono_history_sms_send_pending(modem, &entry->uuid,
-						to, time(NULL), text);
-
-	emit_message_added(sms, m);
+	__ofono_history_sms_send_pending(modem, &uuid, to, time(NULL), text);
 
 	return NULL;
-
-err:
-	return __ofono_error_failed(msg);
 }
 
 static DBusMessage *sms_get_messages(DBusConnection *conn, DBusMessage *msg,
@@ -1081,7 +1050,7 @@
 	while (g_hash_table_iter_next(&hashiter, &key, &value)) {
 		m = value;
 
-		path = message_build_path(sms, m);
+		path = __ofono_sms_message_path_from_uuid(sms, &m->uuid);
 
 		dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT,
 							NULL, &entry);
@@ -1138,7 +1107,11 @@
 	for (l = sms_list; l; l = l->next) {
 		s = l->data;
 
-		sms_encode(s, &len, NULL, buf);
+		if (sms_encode(s, &len, NULL, buf) == FALSE) {
+			g_checksum_free(checksum);
+			return FALSE;
+		}
+
 		g_checksum_update(checksum, buf, len);
 	}
 
@@ -1317,8 +1290,8 @@
 		}
 
 		if (sms_extract_app_port(s, &cdst, &csrc, &is_8bit)) {
-			csrc = is_8bit ? (csrc << 8) : csrc;
-			cdst = is_8bit ? (cdst << 8) : cdst;
+			csrc = is_8bit ? (csrc << 16) : csrc;
+			cdst = is_8bit ? (cdst << 16) : cdst;
 
 			if (l == sms_list) {
 				srcport = csrc;
@@ -1388,21 +1361,21 @@
 			return;
 
 		sms_list = sms_assembly_add_fragment(sms->assembly,
-							incoming, time(NULL),
-							&incoming->deliver.oaddr,
-							ref, max, seq);
+						incoming, time(NULL),
+						&incoming->deliver.oaddr,
+						ref, max, seq);
 
 		if (!sms_list)
 			return;
 
 		sms_dispatch(sms, sms_list);
-		g_slist_foreach(sms_list, (GFunc)g_free, NULL);
+		g_slist_foreach(sms_list, (GFunc) g_free, NULL);
 		g_slist_free(sms_list);
 
 		return;
 	}
 
-	l = g_slist_append(NULL, (void *)incoming);
+	l = g_slist_append(NULL, (void *) incoming);
 	sms_dispatch(sms, l);
 	g_slist_free(l);
 }
@@ -1443,6 +1416,9 @@
 void ofono_sms_deliver_notify(struct ofono_sms *sms, unsigned char *pdu,
 				int len, int tpdu_len)
 {
+	struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom);
+	struct ofono_atom *stk_atom;
+	struct ofono_atom *sim_atom;
 	struct sms s;
 	enum sms_class cls;
 
@@ -1507,13 +1483,36 @@
 		break;
 	case SMS_PID_TYPE_USIM_DOWNLOAD:
 	case SMS_PID_TYPE_ANSI136:
-		if (cls == SMS_CLASS_2) {
-			ofono_error("(U)SIM Download messages not supported");
+		/* If not Class 2, handle in a "normal" way */
+		if (cls != SMS_CLASS_2)
+			break;
+
+		sim_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_SIM);
+
+		if (!sim_atom)
 			return;
-		}
 
-		/* Otherwise handle in a "normal" way */
-		break;
+		if (!__ofono_sim_service_available(
+					__ofono_atom_get_data(sim_atom),
+					SIM_UST_SERVICE_DATA_DOWNLOAD_SMS_PP,
+					SIM_SST_SERVICE_DATA_DOWNLOAD_SMS_PP))
+			return;
+
+		stk_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_STK);
+
+		if (!stk_atom)
+			return;
+
+		__ofono_sms_sim_download(__ofono_atom_get_data(stk_atom),
+						&s, NULL, sms);
+
+		/*
+		 * Passing the USIM response back to network is not
+		 * currently supported
+		 *
+		 * TODO: store in EFsms if not handled
+		 */
+		return;
 	default:
 		break;
 	}
@@ -1603,7 +1602,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -1612,7 +1611,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void sms_unregister(struct ofono_atom *atom)
@@ -1888,18 +1887,42 @@
 	return sms->driver_data;
 }
 
+unsigned short __ofono_sms_get_next_ref(struct ofono_sms *sms)
+{
+	return sms->ref;
+}
+
 int __ofono_sms_txq_submit(struct ofono_sms *sms, GSList *list,
 				unsigned int flags,
 				struct ofono_uuid *uuid,
-				ofono_sms_txq_submit_cb_t cb,
-				void *data, ofono_destroy_func destroy)
+				ofono_sms_txq_queued_cb_t cb, void *data)
 {
+	struct message *m = NULL;
 	struct tx_queue_entry *entry;
 
-	entry = tx_queue_entry_new(list, flags, cb, data, destroy);
+	entry = tx_queue_entry_new(list, flags);
 	if (entry == NULL)
 		return -ENOMEM;
 
+	if (flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS) {
+		m = message_create(&entry->uuid);
+		if (m == NULL)
+			goto err;
+
+		if (message_dbus_register(sms, m) == FALSE)
+			goto err;
+
+		g_hash_table_insert(sms->messages, &m->uuid, m);
+		m->entry = entry;
+	}
+
+	if (list->next != NULL) {
+		if (sms->ref == 65536)
+			sms->ref = 1;
+		else
+			sms->ref = sms->ref + 1;
+	}
+
 	g_queue_push_tail(sms->txq, entry);
 
 	if (g_queue_get_length(sms->txq) == 1)
@@ -1908,20 +1931,36 @@
 	if (uuid)
 		memcpy(uuid, &entry->uuid, sizeof(*uuid));
 
-	if (flags & OFONO_SMS_SUBMIT_FLAG_RECORD_HISTORY) {
-		struct message *m;
+	if (cb)
+		cb(sms, &entry->uuid, data);
 
-		m = message_create(&entry->uuid);
-		if (m == NULL)
-			goto out;
+	if (m && (flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS))
+		emit_message_added(sms, m);
 
-		if (message_dbus_register(sms, m) == FALSE)
-			goto out;
+	return 0;
 
-		g_hash_table_insert(sms->messages, &m->uuid, m);
-		emit_message_added(sms, m);
-	}
+err:
+	tx_queue_entry_destroy(entry);
+
+	return -EINVAL;
+}
+
+int __ofono_sms_txq_set_submit_notify(struct ofono_sms *sms,
+					struct ofono_uuid *uuid,
+					ofono_sms_txq_submit_cb_t cb,
+					void *data,
+					ofono_destroy_func destroy)
+{
+	struct message *m;
+
+	m = g_hash_table_lookup(sms->messages, uuid);
+	if (m == NULL)
+		return -ENOENT;
+
+	if (m->entry == NULL)
+		return -ENOTSUP;
+
+	tx_queue_entry_set_submit_notify(m->entry, cb, data, destroy);
 
-out:
 	return 0;
 }
--- src/smsagent.c
+++ src/smsagent.c
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "smsagent.h"
+
+struct sms_agent {
+	char *interface;
+	char *path;
+	char *service;
+	guint disconnect_watch;
+	ofono_destroy_func removed_cb;
+	void *removed_data;
+	GSList *reqs;
+};
+
+struct sms_agent_request {
+	struct sms_agent *agent;
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	sms_agent_dispatch_cb dispatch_cb;
+	void *dispatch_data;
+	ofono_destroy_func destroy;
+};
+
+static struct sms_agent_request *sms_agent_request_new(struct sms_agent *agent,
+						sms_agent_dispatch_cb cb,
+						void *user_data,
+						ofono_destroy_func destroy)
+{
+	struct sms_agent_request *req;
+
+	req = g_try_new0(struct sms_agent_request, 1);
+	if (!req)
+		return NULL;
+
+	req->agent = agent;
+	req->dispatch_cb = cb;
+	req->dispatch_data = user_data;
+	req->destroy = destroy;
+
+	return req;
+}
+
+static void sms_agent_request_free(struct sms_agent_request *req)
+{
+	if (req->msg) {
+		dbus_message_unref(req->msg);
+		req->msg = NULL;
+	}
+
+	if (req->call) {
+		dbus_pending_call_unref(req->call);
+		req->call = NULL;
+	}
+
+	if (req->destroy)
+		req->destroy(req->dispatch_data);
+
+	g_free(req);
+}
+
+static void sms_agent_send_noreply(struct sms_agent *agent, const char *method)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	DBusMessage *message;
+
+	message = dbus_message_new_method_call(agent->service, agent->path,
+						agent->interface, method);
+	if (!message)
+		return;
+
+	dbus_message_set_no_reply(message, TRUE);
+
+	DBG("Sending: '%s.%s' to '%s' at '%s'", agent->interface, method,
+			agent->service, agent->path);
+
+	g_dbus_send_message(conn, message);
+}
+
+static inline void sms_agent_send_release(struct sms_agent *agent)
+{
+	sms_agent_send_noreply(agent, "Release");
+}
+
+static void sms_agent_disconnect_cb(DBusConnection *conn, void *data)
+{
+	struct sms_agent *agent = data;
+
+	agent->disconnect_watch = 0;
+
+	sms_agent_free(agent);
+}
+
+struct sms_agent *sms_agent_new(const char *interface,
+				const char *service, const char *path)
+{
+	struct sms_agent *agent = g_try_new0(struct sms_agent, 1);
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	if (!agent)
+		return NULL;
+
+	agent->interface = g_strdup(interface);
+	agent->service = g_strdup(service);
+	agent->path = g_strdup(path);
+
+	agent->disconnect_watch = g_dbus_add_disconnect_watch(conn, service,
+							sms_agent_disconnect_cb,
+							agent, NULL);
+
+	return agent;
+}
+
+void sms_agent_set_removed_notify(struct sms_agent *agent,
+					ofono_destroy_func destroy,
+					void *user_data)
+{
+	agent->removed_cb = destroy;
+	agent->removed_data = user_data;
+}
+
+static void sms_agent_request_cancel(gpointer element, gpointer userdata)
+{
+	struct sms_agent_request *req = element;
+
+	dbus_pending_call_cancel(req->call);
+	sms_agent_request_free(req);
+}
+
+void sms_agent_free(struct sms_agent *agent)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	if (!agent)
+		return;
+
+	if (agent->disconnect_watch) {
+		sms_agent_send_release(agent);
+
+		g_dbus_remove_watch(conn, agent->disconnect_watch);
+		agent->disconnect_watch = 0;
+	}
+
+	if (agent->removed_cb)
+		agent->removed_cb(agent->removed_data);
+
+	g_slist_foreach(agent->reqs, sms_agent_request_cancel, NULL);
+	g_slist_free(agent->reqs);
+
+	g_free(agent->path);
+	g_free(agent->service);
+	g_free(agent->interface);
+	g_free(agent);
+}
+
+ofono_bool_t sms_agent_matches(struct sms_agent *agent, const char *service,
+				const char *path)
+{
+	if (path == NULL || service == NULL)
+		return FALSE;
+
+	return g_str_equal(agent->path, path) &&
+			g_str_equal(agent->service, service);
+}
+
+static int check_error(struct sms_agent *agent, DBusMessage *reply,
+				enum sms_agent_result *out_result)
+{
+	DBusError err;
+	int result = 0;
+
+	dbus_error_init(&err);
+
+	if (dbus_set_error_from_message(&err, reply) == FALSE) {
+		*out_result = SMS_AGENT_RESULT_OK;
+		return 0;
+	}
+
+	DBG("SmsAgent %s replied with error %s, %s",
+			agent->path, err.name, err.message);
+
+	/* Timeout is always valid */
+	if (g_str_equal(err.name, DBUS_ERROR_NO_REPLY)) {
+		*out_result = SMS_AGENT_RESULT_TIMEOUT;
+		goto out;
+	}
+
+	result = -EINVAL;
+
+out:
+	dbus_error_free(&err);
+	return result;
+}
+
+static void sms_agent_dispatch_reply_cb(DBusPendingCall *call, void *data)
+{
+	struct sms_agent_request *req = data;
+	struct sms_agent *agent = req->agent;
+	sms_agent_dispatch_cb cb = req->dispatch_cb;
+	void *dispatch_data = req->dispatch_data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(req->call);
+	enum sms_agent_result result;
+
+	if (check_error(agent, reply, &result) == -EINVAL) {
+		dbus_message_unref(reply);
+		sms_agent_free(agent);
+		return;
+	}
+
+	agent->reqs = g_slist_remove(agent->reqs, req);
+	sms_agent_request_free(req);
+
+	if (cb)
+		cb(agent, result, dispatch_data);
+
+	dbus_message_unref(reply);
+}
+
+int sms_agent_dispatch_datagram(struct sms_agent *agent, const char *method,
+				const char *from,
+				const struct tm *remote_sent_time,
+				const struct tm *local_sent_time,
+				const unsigned char *content, unsigned int len,
+				sms_agent_dispatch_cb cb, void *user_data,
+				ofono_destroy_func destroy)
+{
+	struct sms_agent_request *req;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	DBusMessageIter array;
+	char buf[128];
+	const char *str = buf;
+
+	req = sms_agent_request_new(agent, cb, user_data, destroy);
+	if (!req)
+		return -ENOMEM;
+
+	req->msg = dbus_message_new_method_call(agent->service, agent->path,
+						agent->interface, method);
+	if (!req->msg) {
+		sms_agent_request_free(req);
+		return -ENOMEM;
+	}
+
+	dbus_message_iter_init_append(req->msg, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_BYTE_AS_STRING, &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+						&content, len);
+	dbus_message_iter_close_container(&iter, &array);
+
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					OFONO_PROPERTIES_ARRAY_SIGNATURE,
+					&dict);
+
+	strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", local_sent_time);
+	buf[127] = '\0';
+	ofono_dbus_dict_append(&dict, "LocalSentTime", DBUS_TYPE_STRING, &str);
+
+	strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", remote_sent_time);
+	buf[127] = '\0';
+	ofono_dbus_dict_append(&dict, "SentTime", DBUS_TYPE_STRING, &str);
+
+	ofono_dbus_dict_append(&dict, "Sender", DBUS_TYPE_STRING, &from);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	if (!dbus_connection_send_with_reply(conn, req->msg, &req->call, -1)) {
+		ofono_error("Sending D-Bus method failed");
+		sms_agent_request_free(req);
+		return -EIO;
+	}
+
+	agent->reqs = g_slist_append(agent->reqs, req);
+
+	dbus_pending_call_set_notify(req->call, sms_agent_dispatch_reply_cb,
+					req, NULL);
+
+	return 0;
+}
--- src/smsagent.h
+++ src/smsagent.h
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct sms_agent;
+
+enum sms_agent_result {
+	SMS_AGENT_RESULT_OK = 0,
+	SMS_AGENT_RESULT_FAILED,
+	SMS_AGENT_RESULT_TIMEOUT,
+};
+
+typedef void (*sms_agent_dispatch_cb)(struct sms_agent *agent,
+					enum sms_agent_result result,
+					void *data);
+
+struct sms_agent *sms_agent_new(const char *interface,
+					const char *service, const char *path);
+
+void sms_agent_set_removed_notify(struct sms_agent *agent,
+					ofono_destroy_func destroy,
+					void *user_data);
+
+ofono_bool_t sms_agent_matches(struct sms_agent *agent, const char *service,
+				const char *path);
+
+void sms_agent_free(struct sms_agent *agent);
+
+int sms_agent_dispatch_datagram(struct sms_agent *agent, const char *method,
+				const char *from,
+				const struct tm *remote_sent_time,
+				const struct tm *local_sent_time,
+				const unsigned char *content, unsigned int len,
+				sms_agent_dispatch_cb cb, void *user_data,
+				ofono_destroy_func destroy);
--- src/smsutil.c
+++ src/smsutil.c
@@ -158,7 +158,7 @@
 		comp = (dcs & 0x20) ? TRUE : FALSE;
 
 		if (dcs & 0x10)
-			cl = (enum sms_class)(dcs & 0x03);
+			cl = (enum sms_class) (dcs & 0x03);
 		else
 			cl = SMS_CLASS_UNSPECIFIED;
 
@@ -177,7 +177,7 @@
 		else
 			ch = SMS_CHARSET_7BIT;
 
-		cl = (enum sms_class)(dcs & 0x03);
+		cl = (enum sms_class) (dcs & 0x03);
 
 		break;
 	default:
@@ -379,21 +379,39 @@
 	next_octet(pdu, len, offset, &oct);
 	out->year = sms_decode_semi_octet(oct);
 
+	if (out->year > 99)
+		return FALSE;
+
 	next_octet(pdu, len, offset, &oct);
 	out->month = sms_decode_semi_octet(oct);
 
+	if (out->month > 12)
+		return FALSE;
+
 	next_octet(pdu, len, offset, &oct);
 	out->day = sms_decode_semi_octet(oct);
 
+	if (out->day > 31)
+		return FALSE;
+
 	next_octet(pdu, len, offset, &oct);
 	out->hour = sms_decode_semi_octet(oct);
 
+	if (out->hour > 23)
+		return FALSE;
+
 	next_octet(pdu, len, offset, &oct);
 	out->minute = sms_decode_semi_octet(oct);
 
+	if (out->minute > 59)
+		return FALSE;
+
 	next_octet(pdu, len, offset, &oct);
 	out->second = sms_decode_semi_octet(oct);
 
+	if (out->second > 59)
+		return FALSE;
+
 	next_octet(pdu, len, offset, &oct);
 
 	/*
@@ -408,6 +426,9 @@
 	if (oct & 0x08)
 		out->timezone = out->timezone * -1;
 
+	if ((out->timezone > 12*4-1) || (out->timezone < -(12*4-1)))
+		return FALSE;
+
 	return TRUE;
 }
 
@@ -2239,7 +2260,7 @@
 								locking_shift,
 								single_shift);
 		} else {
-			const gchar *from = (const gchar *)(ud + taken);
+			const gchar *from = (const gchar *) (ud + taken);
 			/*
 			 * According to the spec: A UCS2 character shall not be
 			 * split in the middle; if the length of the User Data
@@ -2481,7 +2502,7 @@
 	for (l = assembly->assembly_list; l; l = l->next) {
 		struct sms_assembly_node *node = l->data;
 
-		g_slist_foreach(node->fragment_list, (GFunc)g_free, 0);
+		g_slist_foreach(node->fragment_list, (GFunc) g_free, 0);
 		g_slist_free(node->fragment_list);
 		g_free(node);
 	}
@@ -2632,7 +2653,7 @@
 
 		sms_assembly_backup_free(assembly, node);
 
-		g_slist_foreach(node->fragment_list, (GFunc)g_free, 0);
+		g_slist_foreach(node->fragment_list, (GFunc) g_free, 0);
 		g_slist_free(node->fragment_list);
 		g_free(node);
 
@@ -2739,7 +2760,7 @@
 				g_new0(struct status_report_assembly, 1);
 
 	ret->assembly_table = g_hash_table_new_full(g_str_hash, g_str_equal,
-				g_free, (GDestroyNotify)g_hash_table_destroy);
+				g_free, (GDestroyNotify) g_hash_table_destroy);
 
 	if (imsi) {
 		ret->imsi = imsi;
@@ -3116,17 +3137,142 @@
 }
 
 /*
+ * Prepares a datagram for transmission.  Breaks up into fragments if
+ * necessary using ref as the concatenated message reference number.
+ * Returns a list of sms messages in order.
+ *
+ * @use_delivery_reports: value for the Status-Report-Request field
+ *     (23.040 3.2.9, 9.2.2.2)
+ */
+GSList *sms_datagram_prepare(const char *to,
+				const unsigned char *data, unsigned int len,
+				guint16 ref, gboolean use_16bit_ref,
+				unsigned short src, unsigned short dst,
+				gboolean use_16bit_port,
+				gboolean use_delivery_reports)
+{
+	struct sms template;
+	unsigned int offset;
+	unsigned int written;
+	unsigned int left;
+	guint8 seq;
+	GSList *r = NULL;
+
+	memset(&template, 0, sizeof(struct sms));
+	template.type = SMS_TYPE_SUBMIT;
+	template.submit.rd = FALSE;
+	template.submit.vpf = SMS_VALIDITY_PERIOD_FORMAT_RELATIVE;
+	template.submit.rp = FALSE;
+	template.submit.srr = use_delivery_reports;
+	template.submit.mr = 0;
+	template.submit.vp.relative = 0xA7; /* 24 Hours */
+	template.submit.dcs = 0x04; /* Class Unspecified, 8 Bit */
+	template.submit.udhi = TRUE;
+	sms_address_from_string(&template.submit.daddr, to);
+
+	offset = 1;
+
+	if (use_16bit_port) {
+		template.submit.ud[0] += 6;
+		template.submit.ud[offset] = SMS_IEI_APPLICATION_ADDRESS_16BIT;
+		template.submit.ud[offset + 1] = 4;
+		template.submit.ud[offset + 2] = (dst & 0xff00) >> 8;
+		template.submit.ud[offset + 3] = dst & 0xff;
+		template.submit.ud[offset + 4] = (src & 0xff00) >> 8;
+		template.submit.ud[offset + 5] = src & 0xff;
+
+		offset += 6;
+	} else {
+		template.submit.ud[0] += 4;
+		template.submit.ud[offset] = SMS_IEI_APPLICATION_ADDRESS_8BIT;
+		template.submit.ud[offset + 1] = 2;
+		template.submit.ud[offset + 2] = dst & 0xff;
+		template.submit.ud[offset + 3] = src & 0xff;
+
+		offset += 4;
+	}
+
+	if (len <= (140 - offset)) {
+		template.submit.udl = len + offset;
+		memcpy(template.submit.ud + offset, data, len);
+
+		return sms_list_append(NULL, &template);
+	}
+
+	if (use_16bit_ref) {
+		template.submit.ud[0] += 6;
+		template.submit.ud[offset] = SMS_IEI_CONCATENATED_16BIT;
+		template.submit.ud[offset + 1] = 4;
+		template.submit.ud[offset + 2] = (ref & 0xff00) >> 8;
+		template.submit.ud[offset + 3] = ref & 0xff;
+
+		offset += 6;
+	} else {
+		template.submit.ud[0] += 5;
+		template.submit.ud[offset] = SMS_IEI_CONCATENATED_8BIT;
+		template.submit.ud[offset + 1] = 3;
+		template.submit.ud[offset + 2] = ref & 0xff;
+
+		offset += 5;
+	}
+
+	seq = 0;
+	left = len;
+	written = 0;
+
+	while (left > 0) {
+		unsigned int chunk;
+
+		seq += 1;
+
+		chunk = 140 - offset;
+		if (left < chunk)
+			chunk = left;
+
+		template.submit.udl = chunk + offset;
+		memcpy(template.submit.ud + offset, data + written, chunk);
+
+		written += chunk;
+		left -= chunk;
+
+		template.submit.ud[offset - 1] = seq;
+
+		r = sms_list_append(r, &template);
+
+		if (seq == 255)
+			break;
+	}
+
+	if (left > 0) {
+		g_slist_foreach(r, (GFunc) g_free, NULL);
+		g_slist_free(r);
+
+		return NULL;
+	} else {
+		GSList *l;
+
+		for (l = r; l; l = l->next) {
+			struct sms *sms = l->data;
+
+			sms->submit.ud[offset - 2] = seq;
+		}
+	}
+
+	r = g_slist_reverse(r);
+
+	return r;
+}
+
+/*
  * Prepares the text for transmission.  Breaks up into fragments if
  * necessary using ref as the concatenated message reference number.
- * Returns a list of sms messages in order.  If ref_offset is given,
- * then the ref_offset contains the reference number offset or 0
- * if no concatenation took place.
+ * Returns a list of sms messages in order.
  *
  * @use_delivery_reports: value for the Status-Report-Request field
  *     (23.040 3.2.9, 9.2.2.2)
  */
-GSList *sms_text_prepare(const char *utf8, guint16 ref,
-				gboolean use_16bit, int *ref_offset,
+GSList *sms_text_prepare(const char *to, const char *utf8, guint16 ref,
+				gboolean use_16bit,
 				gboolean use_delivery_reports)
 {
 	struct sms template;
@@ -3146,6 +3292,7 @@
 	template.submit.srr = use_delivery_reports;
 	template.submit.mr = 0;
 	template.submit.vp.relative = 0xA7; /* 24 Hours */
+	sms_address_from_string(&template.submit.daddr, to);
 
 	/* UDHI, UDL, UD and DCS actually depend on what we have in the text */
 	gsm_encoded = convert_utf8_to_gsm(utf8, -1, NULL, &written, 0);
@@ -3170,9 +3317,6 @@
 		template.submit.udhi = FALSE;
 
 	if (gsm_encoded && (written <= sms_text_capacity_gsm(160, offset))) {
-		if (ref_offset)
-			*ref_offset = 0;
-
 		template.submit.udl = written + (offset * 8 + 6) / 7;
 		pack_7bit_own_buf(gsm_encoded, written, offset, FALSE, NULL,
 					0, template.submit.ud + offset);
@@ -3182,9 +3326,6 @@
 	}
 
 	if (ucs2_encoded && (written <= (140 - offset))) {
-		if (ref_offset)
-			*ref_offset = 0;
-
 		template.submit.udl = written + offset;
 		memcpy(template.submit.ud + offset, ucs2_encoded, written);
 
@@ -3197,22 +3338,19 @@
 	if (!offset)
 		offset = 1;
 
-	if (ref_offset)
-		*ref_offset = offset + 2;
-
 	if (use_16bit) {
 		template.submit.ud[0] += 6;
 		template.submit.ud[offset] = SMS_IEI_CONCATENATED_16BIT;
 		template.submit.ud[offset + 1] = 4;
-		template.submit.ud[offset + 2] = (ref & 0xf0) >> 8;
-		template.submit.ud[offset + 3] = ref & 0xf;
+		template.submit.ud[offset + 2] = (ref & 0xff00) >> 8;
+		template.submit.ud[offset + 3] = ref & 0xff;
 
 		offset += 6;
 	} else {
 		template.submit.ud[0] += 5;
 		template.submit.ud[offset] = SMS_IEI_CONCATENATED_8BIT;
 		template.submit.ud[offset + 1] = 3;
-		template.submit.ud[offset + 2] = ref & 0xf;
+		template.submit.ud[offset + 2] = ref & 0xff;
 
 		offset += 5;
 	}
@@ -3269,7 +3407,7 @@
 		g_free(ucs2_encoded);
 
 	if (left > 0) {
-		g_slist_foreach(r, (GFunc)g_free, NULL);
+		g_slist_foreach(r, (GFunc) g_free, NULL);
 		g_slist_free(r);
 
 		return NULL;
@@ -3308,7 +3446,7 @@
 	case 0:
 		ch = SMS_CHARSET_7BIT;
 		cl = SMS_CLASS_UNSPECIFIED;
-		lang = (enum cbs_language)lower;
+		lang = (enum cbs_language) lower;
 		break;
 	case 1:
 		if (lower > 1)
@@ -3329,7 +3467,7 @@
 
 		ch = SMS_CHARSET_7BIT;
 		cl = SMS_CLASS_UNSPECIFIED;
-		lang = (enum cbs_language)dcs;
+		lang = (enum cbs_language) dcs;
 		break;
 	case 4:
 	case 5:
@@ -3338,21 +3476,21 @@
 		comp = (dcs & 0x20) ? TRUE : FALSE;
 
 		if (dcs & 0x10)
-			cl = (enum sms_class)(dcs & 0x03);
+			cl = (enum sms_class) (dcs & 0x03);
 		else
 			cl = SMS_CLASS_UNSPECIFIED;
 
 		if (((dcs & 0x0c) >> 2) < 3)
-			ch = (enum sms_charset)((dcs & 0x0c) >> 2);
+			ch = (enum sms_charset) ((dcs & 0x0c) >> 2);
 		else
 			return FALSE;
 
 		break;
 	case 9:
 		udh = TRUE;
-		cl = (enum sms_class)(dcs & 0x03);
+		cl = (enum sms_class) (dcs & 0x03);
 		if (((dcs & 0x0c) >> 2) < 3)
-			ch = (enum sms_charset)((dcs & 0x0c) >> 2);
+			ch = (enum sms_charset) ((dcs & 0x0c) >> 2);
 		else
 			return FALSE;
 
@@ -3367,7 +3505,7 @@
 			ch = SMS_CHARSET_7BIT;
 
 		if (lower & 0x3)
-			cl = (enum sms_class)(lower & 0x3);
+			cl = (enum sms_class) (lower & 0x3);
 		else
 			cl = SMS_CLASS_UNSPECIFIED;
 
@@ -3720,7 +3858,7 @@
 	if (charset == SMS_CHARSET_7BIT)
 		utf8 = convert_gsm_to_utf8(buf, bufsize, NULL, NULL, 0);
 	else
-		utf8 = g_convert((char *)buf, bufsize, "UTF-8//TRANSLIT",
+		utf8 = g_convert((char *) buf, bufsize, "UTF-8//TRANSLIT",
 					"UCS-2BE", NULL, NULL, NULL);
 
 	g_free(buf);
@@ -3759,7 +3897,7 @@
 	for (l = assembly->assembly_list; l; l = l->next) {
 		struct cbs_assembly_node *node = l->data;
 
-		g_slist_foreach(node->pages, (GFunc)g_free, 0);
+		g_slist_foreach(node->pages, (GFunc) g_free, 0);
 		g_slist_free(node->pages);
 		g_free(node);
 	}
@@ -3839,7 +3977,7 @@
 		else
 			assembly->assembly_list = l->next;
 
-		g_slist_foreach(node->pages, (GFunc)g_free, NULL);
+		g_slist_foreach(node->pages, (GFunc) g_free, NULL);
 		g_slist_free(node->pages);
 		g_free(node->pages);
 		tmp = l;
@@ -4149,7 +4287,7 @@
 	}
 
 	tmp = cbs_optimize_ranges(ret);
-	g_slist_foreach(ret, (GFunc)g_free, NULL);
+	g_slist_foreach(ret, (GFunc) g_free, NULL);
 	g_slist_free(ret);
 
 	return tmp;
--- src/smsutil.h
+++ src/smsutil.h
@@ -516,8 +516,15 @@
 void status_report_assembly_expire(struct status_report_assembly *assembly,
 					time_t before);
 
-GSList *sms_text_prepare(const char *utf8, guint16 ref,
-				gboolean use_16bit, int *ref_offset,
+GSList *sms_text_prepare(const char *to, const char *utf8, guint16 ref,
+				gboolean use_16bit,
+				gboolean use_delivery_reports);
+
+GSList *sms_datagram_prepare(const char *to,
+				const unsigned char *data, unsigned int len,
+				guint16 ref, gboolean use_16bit_ref,
+				unsigned short src, unsigned short dst,
+				gboolean use_16bit_port,
 				gboolean use_delivery_reports);
 
 gboolean cbs_dcs_decode(guint8 dcs, gboolean *udhi, enum sms_class *cls,
--- src/ssn.c
+++ src/ssn.c
@@ -150,7 +150,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -159,7 +159,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void ssn_unregister(struct ofono_atom *atom)
--- src/stk.c
+++ src/stk.c
@@ -75,6 +75,9 @@
 	struct stk_icon_id idle_mode_icon;
 	struct timeval get_inkey_start_ts;
 	int dtmf_id;
+
+	__ofono_sms_sim_download_cb_t sms_pp_cb;
+	void *sms_pp_userdata;
 };
 
 struct envelope_op {
@@ -257,6 +260,42 @@
 		stk_cbs_download_cb(stk, FALSE, NULL, -1);
 }
 
+static void stk_sms_download_cb(struct ofono_stk *stk, gboolean ok,
+				const unsigned char *data, int len)
+{
+	DBG("SMS-PP download to UICC reported %s", ok ? "success" : "error");
+
+	if (stk->sms_pp_cb)
+		stk->sms_pp_cb(ok, data, len, stk->sms_pp_userdata);
+}
+
+int __ofono_sms_sim_download(struct ofono_stk *stk, const struct sms *msg,
+				__ofono_sms_sim_download_cb_t cb, void *data)
+{
+	struct stk_envelope e;
+
+	if (msg->type != SMS_TYPE_DELIVER)
+		return -EINVAL;
+
+	DBG("");
+
+	memset(&e, 0, sizeof(e));
+
+	e.type = STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD;
+	e.src = STK_DEVICE_IDENTITY_TYPE_NETWORK;
+
+	e.sms_pp_download.address.number = (char *) msg->sc_addr.address;
+	e.sms_pp_download.address.ton_npi = msg->sc_addr.numbering_plan |
+		(msg->sc_addr.number_type << 4);
+	memcpy(&e.sms_pp_download.message, &msg->deliver, sizeof(msg->deliver));
+
+	stk->sms_pp_cb = cb;
+	stk->sms_pp_userdata = data;
+
+	return stk_send_envelope(stk, &e, stk_sms_download_cb,
+					ENVELOPE_RETRIES_DEFAULT);
+}
+
 static char *dbus_apply_text_attributes(const char *text,
 					const struct stk_text_attribute *attr)
 {
@@ -264,6 +303,9 @@
 	const uint8_t *j = attr->attributes;
 	const uint8_t *end = j + attr->len;
 
+	if (text == NULL)
+		return NULL;
+
 	if (attr->len & 3)
 		return NULL;
 
@@ -437,6 +479,24 @@
 	/* TODO */
 }
 
+static int duration_to_msecs(const struct stk_duration *duration)
+{
+	int msecs = duration->interval;
+
+	switch (duration->unit) {
+	case STK_DURATION_TYPE_MINUTES:
+		msecs *= 60;
+		/* Fall through.  */
+	case STK_DURATION_TYPE_SECONDS:
+		msecs *= 10;
+		/* Fall through.  */
+	case STK_DURATION_TYPE_SECOND_TENTHS:
+		msecs *= 100;
+	}
+
+	return msecs;
+}
+
 static DBusMessage *stk_get_properties(DBusConnection *conn,
 					DBusMessage *msg, void *data)
 {
@@ -774,6 +834,7 @@
 	struct ofono_atom *sms_atom;
 	struct ofono_sms *sms;
 	GSList msg_list;
+	struct ofono_uuid uuid;
 
 	sms_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_SMS);
 
@@ -789,13 +850,13 @@
 	msg_list.data = (void *) &cmd->send_sms.gsm_sms;
 	msg_list.next = NULL;
 
-	if (__ofono_sms_txq_submit(sms, &msg_list, 0, NULL, send_sms_submit_cb,
-				stk->extern_req, g_free) < 0) {
-		g_free(stk->extern_req);
+	if (__ofono_sms_txq_submit(sms, &msg_list, 0, &uuid, NULL, NULL) < 0) {
 		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
 		return TRUE;
 	}
 
+	__ofono_sms_txq_set_submit_notify(sms, &uuid, send_sms_submit_cb,
+						stk->extern_req, g_free);
 	stk->cancel_cmd = send_sms_cancel;
 
 	stk_alpha_id_set(stk, cmd->send_sms.alpha_id, &cmd->send_sms.text_attr,
@@ -804,6 +865,7 @@
 	return FALSE;
 }
 
+/* Note: may be called from ofono_stk_proactive_command_handled_notify */
 static gboolean handle_command_set_idle_text(const struct stk_command *cmd,
 						struct stk_response *rsp,
 						struct ofono_stk *stk)
@@ -1000,23 +1062,14 @@
 	struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom);
 	int seconds;
 
-	switch (cmd->poll_interval.duration.unit) {
-	case STK_DURATION_TYPE_MINUTES:
-		seconds = cmd->poll_interval.duration.interval * 60;
-		break;
-	case STK_DURATION_TYPE_SECONDS:
-		seconds = cmd->poll_interval.duration.interval;
-		break;
-	case STK_DURATION_TYPE_SECOND_TENTHS:
-		seconds = (4 + cmd->poll_interval.duration.interval) / 10;
-		if (seconds < 1)
-			seconds = 1;
-		break;
-	default:
+	if (!cmd->poll_interval.duration.interval) {
 		rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
 		return TRUE;
 	}
 
+	seconds = MAX(duration_to_msecs(&cmd->poll_interval.duration) / 1000,
+			1);
+
 	ofono_modem_set_integer(modem, "status-poll-interval", seconds);
 
 	if (seconds > 255) {
@@ -1032,6 +1085,7 @@
 	return TRUE;
 }
 
+/* Note: may be called from ofono_stk_proactive_command_handled_notify */
 static gboolean handle_command_set_up_menu(const struct stk_command *cmd,
 						struct stk_response *rsp,
 						struct ofono_stk *stk)
@@ -1219,17 +1273,8 @@
 		return TRUE;
 	}
 
-	if (dt->duration.interval) {
-		timeout = dt->duration.interval;
-		switch (dt->duration.unit) {
-		case STK_DURATION_TYPE_MINUTES:
-			timeout *= 60;
-		case STK_DURATION_TYPE_SECONDS:
-			timeout *= 10;
-		case STK_DURATION_TYPE_SECOND_TENTHS:
-			timeout *= 100;
-		}
-	}
+	if (dt->duration.interval)
+		timeout = duration_to_msecs(&dt->duration);
 
 	err = stk_agent_display_text(stk->current_agent, text, &dt->icon_id,
 					priority, display_text_cb, stk,
@@ -1387,17 +1432,8 @@
 		return TRUE;
 	}
 
-	if (gi->duration.interval) {
-		timeout = gi->duration.interval;
-		switch (gi->duration.unit) {
-		case STK_DURATION_TYPE_MINUTES:
-			timeout *= 60;
-		case STK_DURATION_TYPE_SECONDS:
-			timeout *= 10;
-		case STK_DURATION_TYPE_SECOND_TENTHS:
-			timeout *= 100;
-		}
-	}
+	if (gi->duration.interval)
+		timeout = duration_to_msecs(&gi->duration);
 
 	gettimeofday(&stk->get_inkey_start_ts, NULL);
 
@@ -2090,6 +2126,132 @@
 	return FALSE;
 }
 
+static void play_tone_cb(enum stk_agent_result result, void *user_data)
+{
+	struct ofono_stk *stk = user_data;
+
+	stk->respond_on_exit = FALSE;
+
+	switch (result) {
+	case STK_AGENT_RESULT_OK:
+	case STK_AGENT_RESULT_TIMEOUT:
+		send_simple_response(stk, STK_RESULT_TYPE_SUCCESS);
+		break;
+
+	default:
+		send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+		break;
+	}
+}
+
+static gboolean handle_command_play_tone(const struct stk_command *cmd,
+						struct stk_response *rsp,
+						struct ofono_stk *stk)
+{
+	static int manufacturer_timeout = 10000; /* 10 seconds */
+	static const struct {
+		const char *name;
+		/* Continuous true/false according to 02.40 */
+		gboolean continuous;
+	} tone_infos[] = {
+		/* Default */
+		[0x00] = { "general-beep", FALSE },
+
+		/* Standard */
+		[0x01] = { "dial-tone", TRUE },
+		[0x02] = { "busy", TRUE },
+		[0x03] = { "congestion", TRUE },
+		[0x04] = { "radio-path-acknowledge", FALSE },
+		[0x05] = { "radio-path-not-available", FALSE },
+		[0x06] = { "error", TRUE },
+		[0x07] = { "call-waiting", FALSE },
+		[0x08] = { "ringing-tone", TRUE },
+
+		/* Proprietary */
+		[0x10] = { "general-beep", FALSE },
+		[0x11] = { "positive-acknowledgement", FALSE },
+		[0x12] = { "negative-acknowledgement", FALSE },
+		[0x13] = { "user-ringing-tone", TRUE },
+		[0x14] = { "user-sms-alert", FALSE },
+		[0x15] = { "critical", FALSE },
+		[0x20] = { "vibrate", TRUE },
+
+		/* Themed */
+		[0x30] = { "happy", FALSE },
+		[0x31] = { "sad", FALSE },
+		[0x32] = { "urgent-action", FALSE },
+		[0x33] = { "question", FALSE },
+		[0x34] = { "message-received", FALSE },
+
+		/* Melody */
+		[0x40] = { "melody-1", FALSE },
+		[0x41] = { "melody-2", FALSE },
+		[0x42] = { "melody-3", FALSE },
+		[0x43] = { "melody-4", FALSE },
+		[0x44] = { "melody-5", FALSE },
+		[0x45] = { "melody-6", FALSE },
+		[0x46] = { "melody-7", FALSE },
+		[0x47] = { "melody-8", FALSE },
+	};
+
+	const struct stk_command_play_tone *pt = &cmd->play_tone;
+	uint8_t qualifier = stk->pending_cmd->qualifier;
+	gboolean vibrate = (qualifier & (1 << 0)) != 0;
+	char *text;
+	int timeout;
+	int err;
+
+	if (pt->tone > sizeof(tone_infos) / sizeof(*tone_infos) ||
+			!tone_infos[pt->tone].name) {
+		rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+
+		return TRUE;
+	}
+
+	text = dbus_apply_text_attributes(pt->alpha_id ? pt->alpha_id : "",
+						&pt->text_attr);
+	if (!text) {
+		rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+
+		return TRUE;
+	}
+
+	if (pt->duration.interval)
+		timeout = duration_to_msecs(&pt->duration);
+	else
+		timeout = manufacturer_timeout;
+
+	if (!tone_infos[pt->tone].continuous)
+		/* Duration ignored */
+		err = stk_agent_play_tone(stk->current_agent, text,
+						&pt->icon_id, vibrate,
+						tone_infos[pt->tone].name,
+						play_tone_cb, stk, NULL,
+						stk->timeout * 1000);
+	else
+		err = stk_agent_loop_tone(stk->current_agent, text,
+						&pt->icon_id, vibrate,
+						tone_infos[pt->tone].name,
+						play_tone_cb, stk, NULL,
+						timeout);
+
+	g_free(text);
+
+	if (err < 0) {
+		/*
+		 * We most likely got an out of memory error, tell SIM
+		 * to retry
+		 */
+		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
+		return TRUE;
+	}
+
+	stk->respond_on_exit = TRUE;
+	stk->cancel_cmd = stk_request_cancel;
+
+	return FALSE;
+}
+
 static void stk_proactive_command_cancel(struct ofono_stk *stk)
 {
 	if (stk->immediate_response)
@@ -2267,6 +2429,11 @@
 							&rsp, stk);
 		break;
 
+	case STK_COMMAND_TYPE_PLAY_TONE:
+		respond = handle_command_play_tone(stk->pending_cmd,
+							&rsp, stk);
+		break;
+
 	default:
 		rsp.result.type = STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD;
 		break;
@@ -2285,6 +2452,18 @@
 						const unsigned char *pdu)
 {
 	struct stk_command *cmd;
+	struct stk_response dummyrsp;
+
+	/*
+	 * Modems send us the proactive command details and terminal responses
+	 * sent by the modem as a response to the command.  Terminal responses
+	 * start with the Command Details CTLV tag (0x81).  We filter terminal
+	 * responses here
+	 */
+	if (length > 0 && pdu[0] == 0x81) {
+		stk_alpha_id_unset(stk);
+		return;
+	}
 
 	stk_proactive_command_cancel(stk);
 
@@ -2309,18 +2488,40 @@
 				&cmd->send_sms.text_attr,
 				&cmd->send_sms.icon_id);
 		break;
+
+	case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT:
+		handle_command_set_idle_text(cmd, &dummyrsp, stk);
+		break;
+
+	case STK_COMMAND_TYPE_SETUP_MENU:
+		handle_command_set_up_menu(cmd, &dummyrsp, stk);
+		break;
+
+	case STK_COMMAND_TYPE_SETUP_CALL:
+		/* TODO */
+		break;
+
+	case STK_COMMAND_TYPE_SEND_USSD:
+		stk_alpha_id_set(stk, cmd->send_ussd.alpha_id,
+				&cmd->send_ussd.text_attr,
+				&cmd->send_ussd.icon_id);
+		break;
+
+	case STK_COMMAND_TYPE_SEND_SS:
+		stk_alpha_id_set(stk, cmd->send_ss.alpha_id,
+				&cmd->send_ss.text_attr,
+				&cmd->send_ss.icon_id);
+
+	case STK_COMMAND_TYPE_SEND_DTMF:
+		stk_alpha_id_set(stk, cmd->send_dtmf.alpha_id,
+				&cmd->send_dtmf.text_attr,
+				&cmd->send_dtmf.icon_id);
+		break;
 	}
 
 	stk_command_free(cmd);
 }
 
-void ofono_stk_terminal_response_sent_notify(struct ofono_stk *stk,
-						int length,
-						const unsigned char *pdu)
-{
-	stk_alpha_id_unset(stk);
-}
-
 int ofono_stk_driver_register(const struct ofono_stk_driver *d)
 {
 	DBG("driver: %p, name: %s", d, d->name);
@@ -2328,7 +2529,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -2337,7 +2538,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void stk_unregister(struct ofono_atom *atom)
--- src/stkutil.c
+++ src/stkutil.c
@@ -640,7 +640,7 @@
 	return TRUE;
 
 error:
-	g_slist_foreach(*fl, (GFunc)g_free, NULL);
+	g_slist_foreach(*fl, (GFunc) g_free, NULL);
 	g_slist_free(*fl);
 	return FALSE;
 }
@@ -2237,7 +2237,7 @@
 	if (count == 1)
 		return TRUE;
 
-	g_slist_foreach(list, (GFunc)destroy_stk_item, NULL);
+	g_slist_foreach(list, (GFunc) destroy_stk_item, NULL);
 	g_slist_free(list);
 	return FALSE;
 
@@ -2358,7 +2358,7 @@
 			minimum_set = FALSE;
 	}
 
-	g_slist_foreach(entries, (GFunc)g_free, NULL);
+	g_slist_foreach(entries, (GFunc) g_free, NULL);
 	g_slist_free(entries);
 
 	if (minimum_set == FALSE)
@@ -2543,7 +2543,7 @@
 {
 	g_free(command->setup_menu.alpha_id);
 	g_slist_foreach(command->setup_menu.items,
-				(GFunc)destroy_stk_item, NULL);
+				(GFunc) destroy_stk_item, NULL);
 	g_slist_free(command->setup_menu.items);
 }
 
@@ -2585,7 +2585,7 @@
 {
 	g_free(command->select_item.alpha_id);
 	g_slist_foreach(command->select_item.items,
-				(GFunc)destroy_stk_item, NULL);
+				(GFunc) destroy_stk_item, NULL);
 	g_slist_free(command->select_item.items);
 }
 
@@ -2847,7 +2847,7 @@
 
 static void destroy_refresh(struct stk_command *command)
 {
-	g_slist_foreach(command->refresh.file_list, (GFunc)g_free, NULL);
+	g_slist_foreach(command->refresh.file_list, (GFunc) g_free, NULL);
 	g_slist_free(command->refresh.file_list);
 	g_free(command->refresh.alpha_id);
 }
@@ -3144,7 +3144,7 @@
 	g_free(command->launch_browser.url);
 	g_free(command->launch_browser.bearer.array);
 	g_slist_foreach(command->launch_browser.prov_file_refs,
-				(GFunc)g_free, NULL);
+				(GFunc) g_free, NULL);
 	g_slist_free(command->launch_browser.prov_file_refs);
 	g_free(command->launch_browser.text_gateway_proxy_id);
 	g_free(command->launch_browser.alpha_id);
@@ -3448,7 +3448,7 @@
 {
 	g_free(command->retrieve_mms.alpha_id);
 	g_slist_foreach(command->retrieve_mms.mms_rec_files,
-						(GFunc)g_free, NULL);
+						(GFunc) g_free, NULL);
 	g_slist_free(command->retrieve_mms.mms_rec_files);
 }
 
@@ -3492,7 +3492,7 @@
 {
 	g_free(command->submit_mms.alpha_id);
 	g_slist_foreach(command->submit_mms.mms_subm_files,
-						(GFunc)g_free, NULL);
+						(GFunc) g_free, NULL);
 	g_slist_free(command->submit_mms.mms_subm_files);
 }
 
@@ -3529,7 +3529,7 @@
 static void destroy_display_mms(struct stk_command *command)
 {
 	g_slist_foreach(command->display_mms.mms_subm_files,
-						(GFunc)g_free, NULL);
+						(GFunc) g_free, NULL);
 	g_slist_free(command->display_mms.mms_subm_files);
 }
 
--- src/ussd.c
+++ src/ussd.c
@@ -759,7 +759,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -768,7 +768,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void ussd_unregister(struct ofono_atom *atom)
@@ -778,11 +778,11 @@
 	struct ofono_modem *modem = __ofono_atom_get_modem(atom);
 	const char *path = __ofono_atom_get_path(atom);
 
-	g_slist_foreach(ussd->ss_control_list, (GFunc)ssc_entry_destroy, NULL);
+	g_slist_foreach(ussd->ss_control_list, (GFunc) ssc_entry_destroy, NULL);
 	g_slist_free(ussd->ss_control_list);
 	ussd->ss_control_list = NULL;
 
-	g_slist_foreach(ussd->ss_passwd_list, (GFunc)ssc_entry_destroy, NULL);
+	g_slist_foreach(ussd->ss_passwd_list, (GFunc) ssc_entry_destroy, NULL);
 	g_slist_free(ussd->ss_passwd_list);
 	ussd->ss_passwd_list = NULL;
 
--- src/util.c
+++ src/util.c
@@ -462,8 +462,8 @@
 
 static int compare_codepoints(const void *a, const void *b)
 {
-	const struct codepoint *ca = (const struct codepoint *)a;
-	const struct codepoint *cb = (const struct codepoint *)b;
+	const struct codepoint *ca = (const struct codepoint *) a;
+	const struct codepoint *cb = (const struct codepoint *) b;
 
 	return (ca->from > cb->from) - (ca->from < cb->from);
 }
@@ -956,7 +956,7 @@
 	 * character.
 	 */
 	if (ussd && (((out - buf) % 8) == 0) && (*(out - 1) == '\r'))
-			out = out - 1;
+		out = out - 1;
 
 	if (terminator)
 		*out = terminator;
@@ -1126,7 +1126,7 @@
 			if (buffer[i] == 0xff && buffer[i + 1] == 0xff)
 				break;
 
-		return g_convert((char *)buffer + 1, i - 1,
+		return g_convert((char *) buffer + 1, i - 1,
 					"UTF-8//TRANSLIT", "UCS-2BE",
 					NULL, NULL, NULL);
 	case 0x81:
--- src/voicecall.c
+++ src/voicecall.c
@@ -1926,7 +1926,7 @@
 {
 	int i = 0;
 
-	g_slist_foreach(vc->en_list, (GFunc)g_free, NULL);
+	g_slist_foreach(vc->en_list, (GFunc) g_free, NULL);
 	g_slist_free(vc->en_list);
 	vc->en_list = NULL;
 
@@ -2026,7 +2026,7 @@
 	if (d->probe == NULL)
 		return -EINVAL;
 
-	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+	g_drivers = g_slist_prepend(g_drivers, (void *) d);
 
 	return 0;
 }
@@ -2035,7 +2035,7 @@
 {
 	DBG("driver: %p, name: %s", d, d->name);
 
-	g_drivers = g_slist_remove(g_drivers, (void *)d);
+	g_drivers = g_slist_remove(g_drivers, (void *) d);
 }
 
 static void voicecall_unregister(struct ofono_atom *atom)
@@ -2077,13 +2077,13 @@
 		vc->driver->remove(vc);
 
 	if (vc->en_list) {
-		g_slist_foreach(vc->en_list, (GFunc)g_free, NULL);
+		g_slist_foreach(vc->en_list, (GFunc) g_free, NULL);
 		g_slist_free(vc->en_list);
 		vc->en_list = NULL;
 	}
 
 	if (vc->new_en_list) {
-		g_slist_foreach(vc->new_en_list, (GFunc)g_free, NULL);
+		g_slist_foreach(vc->new_en_list, (GFunc) g_free, NULL);
 		g_slist_free(vc->new_en_list);
 		vc->new_en_list = NULL;
 	}
@@ -2472,7 +2472,7 @@
 {
 	struct ofono_voicecall *vc = user_data;
 	struct tone_queue_entry *entry = g_queue_peek_head(vc->toneq);
-	char buf[256];
+	char final;
 	unsigned len;
 
 	vc->tone_source = 0;
@@ -2483,14 +2483,17 @@
 	len = strcspn(entry->left, "pP");
 
 	if (len) {
-		if (len >= sizeof(buf))
-			len = sizeof(buf) - 1;
+		if (len > 8) /* Arbitrary length limit per request */
+			len = 8;
 
-		memcpy(buf, entry->left, len);
-		buf[len] = '\0';
-		entry->left += len;
+		/* Temporarily move the end of the string */
+		final = entry->left[len];
+		entry->left[len] = '\0';
+
+		vc->driver->send_tones(vc, entry->left, tone_request_cb, vc);
 
-		vc->driver->send_tones(vc, buf, tone_request_cb, vc);
+		entry->left += len;
+		entry->left[0] = final;
 	} else
 		tone_request_cb(NULL, vc);
 
--- test/create-context
+++ test/create-context
-#!/usr/bin/python
-
-import sys
-import dbus
-
-bus = dbus.SystemBus()
-
-manager = dbus.Interface(bus.get_object('org.ofono', '/'),
-						'org.ofono.Manager')
-
-modems = manager.GetModems()
-
-for path, properties in modems:
-	if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
-		continue
-
-	connman = dbus.Interface(bus.get_object('org.ofono', path),
-					'org.ofono.ConnectionManager')
-
-	contexts = connman.GetContexts()
-
-	if (len(contexts) == 0):
-		path = connman.AddContext("internet")
-	else:
-		path = contexts[0][0]
-
-	context = dbus.Interface(bus.get_object('org.ofono', path),
-					'org.ofono.ConnectionContext')
-
-	try:
-		context.SetProperty("AccessPointName", sys.argv[1])
-	except IndexError:
-		print "Usage: %s <apn_name>" % sys.argv[0]
-		exit(1)
-
-	print "Setting APN of %s to %s" % (path, sys.argv[1])
--- test/create-internet-context
+++ test/create-internet-context
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+						'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+	if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+		continue
+
+	connman = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.ConnectionManager')
+
+	contexts = connman.GetContexts()
+	path = "";
+
+	for i, properties in contexts:
+		if properties["Type"] == "internet":
+			path = i
+			break
+
+	if path == "":
+		path = connman.AddContext("internet")
+		print "Created new context %s" % (path)
+	else:
+		print "Found context %s" % (path)
+
+	context = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.ConnectionContext')
+
+	if len(sys.argv) > 1:
+		context.SetProperty("AccessPointName", sys.argv[1])
+		print "Setting APN to %s" % (sys.argv[1])
+
+	if len(sys.argv) > 2:
+		context.SetProperty("Username", sys.argv[2])
+		print "Setting username to %s" % (sys.argv[2])
+
+	if len(sys.argv) > 3:
+		context.SetProperty("Password", sys.argv[3])
+		print "Setting password to %s" % (sys.argv[3])
--- test/create-mms-context
+++ test/create-mms-context
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+						'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+	if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+		continue
+
+	connman = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.ConnectionManager')
+
+	contexts = connman.GetContexts()
+	path = "";
+
+	for i, properties in contexts:
+		if properties["Type"] == "mms":
+			path = i
+			break
+
+	if path == "":
+		path = connman.AddContext("mms")
+		print "Created new context %s" % (path)
+	else:
+		print "Found context %s" % (path)
+
+	context = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.ConnectionContext')
+
+	if len(sys.argv) > 1:
+		context.SetProperty("AccessPointName", sys.argv[1])
+		print "Setting APN to %s" % (sys.argv[1])
+
+	if len(sys.argv) > 2:
+		context.SetProperty("Username", sys.argv[2])
+		print "Setting username to %s" % (sys.argv[2])
+
+	if len(sys.argv) > 3:
+		context.SetProperty("Password", sys.argv[3])
+		print "Setting password to %s" % (sys.argv[3])
--- test/deactivate-all
+++ test/deactivate-all
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+						'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+	if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+		continue
+
+	connman = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.ConnectionManager')
+
+	connman.DeactivateAll()
--- test/monitor-ofono
+++ test/monitor-ofono
@@ -75,6 +75,10 @@
 	iface = interface[interface.rfind(".") + 1:]
 	print "{%s} [%s] %s %s" % (iface, path, member, str(msg))
 
+def value(value, member, path, interface):
+	iface = interface[interface.rfind(".") + 1:]
+	print "{%s} [%s] %s %s" % (iface, path, member, str(value))
+
 if __name__ == '__main__':
 	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 
@@ -149,6 +153,13 @@
 						member_keyword="member",
 						path_keyword="path",
 						interface_keyword="interface")
+
+	bus.add_signal_receiver(value,
+				bus_name="org.ofono",
+					signal_name = "DisconnectReason",
+						member_keyword="member",
+						path_keyword="path",
+						interface_keyword="interface")
 
 	for member in ["IncomingBroadcast", "EmergencyBroadcast",
 			"IncomingMessage", "ImmediateMessage"]:
--- test/process-context-settings
+++ test/process-context-settings
@@ -37,6 +37,7 @@
 		if settings["Method"] == "dhcp":
 			print "    Run DHCP on interface %s" % (interface)
 		else:
+			print "    Interface is %s" % (interface)
 			print "    IP address is %s" % (address)
 			print "    Gateway is %s" % (gateway)
 
--- test/reset-pin
+++ test/reset-pin
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 5:
+	path, puk_type, puk, pin = sys.argv[1:]
+elif len(sys.argv) == 4:
+	manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+					'org.ofono.Manager')
+	modems = manager.GetModems()
+	path = modems[0][0]
+	puk_type, puk, pin = sys.argv[1:]
+else:
+	print "%s [PATH] puk_type puk pin" % (sys.argv[0])
+
+print "Reset pin for modem %s..." % path
+simmanager = dbus.Interface(bus.get_object('org.ofono', path),
+				'org.ofono.SimManager')
+
+simmanager.ResetPin(puk_type, puk, pin)
--- test/send-vcard
+++ test/send-vcard
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+						'org.ofono.Manager')
+
+modems = manager.GetModems()
+path = modems[0][0]
+
+manager = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.SmartMessaging')
+
+vcard = file(sys.argv[2]).read()
+path = manager.SendBusinessCard(sys.argv[1], vcard)
+
+print path
--- test/set-context
+++ test/set-context
-#!/usr/bin/python
-
-import sys
-import dbus
-
-bus = dbus.SystemBus()
-
-manager = dbus.Interface(bus.get_object('org.ofono', '/'),
-						'org.ofono.Manager')
-
-modems = manager.GetModems()
-
-for path, properties in modems:
-	if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
-		continue
-
-	connman = dbus.Interface(bus.get_object('org.ofono', path),
-					'org.ofono.ConnectionManager')
-
-	contexts = connman.GetContexts()
-
-	if len(contexts) < 1:
-		print "No context available"
-		exit(1)
-	else:
-		path = contexts[0][0]
-
-	context = dbus.Interface(bus.get_object('org.ofono', path),
-					'org.ofono.ConnectionContext')
-
-	try:
-		context.SetProperty("AccessPointName", sys.argv[1])
-		if len(sys.argv) > 2:
-			context.SetProperty("Username", sys.argv[2])
-		if len(sys.argv) > 3:
-			context.SetProperty("Password", sys.argv[3])
-	except IndexError:
-		print "Usage: %s <apn_name> [username] [password]" % sys.argv[0]
-		exit(1)
-
-	print "Setting APN of %s to %s" % (path, sys.argv[1])
--- test/set-fast-dormancy
+++ test/set-fast-dormancy
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 3:
+	path = sys.argv[1]
+	enable = int(sys.argv[2])
+elif len(sys.argv) == 2:
+	manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+						'org.ofono.Manager')
+	modems = manager.GetModems()
+	path = modems[0][0]
+	enable = int(sys.argv[1])
+else:
+	print "%s [PATH] {0|1}" % (sys.argv[0])
+	exit(1)
+
+print "Setting fast dormancy for modem %s..." % path
+radiosettings = dbus.Interface(bus.get_object('org.ofono', path),
+						'org.ofono.RadioSettings')
+
+radiosettings.SetProperty("FastDormancy", dbus.Boolean(enable));
--- test/set-mms-details
+++ test/set-mms-details
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+						'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+	if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+		continue
+
+	connman = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.ConnectionManager')
+
+	contexts = connman.GetContexts()
+	path = "";
+
+	for i, properties in contexts:
+		if properties["Type"] == "mms":
+			path = i
+			break
+
+	if path == "":
+		print "No MMS context"
+		exit(1)
+
+	context = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.ConnectionContext')
+
+	if len(sys.argv) < 3:
+		print "Usage: %s <proxy> <center>" % (sys.argv[0])
+		exit(1)
+
+	context.SetProperty("MessageProxy", sys.argv[1])
+	print "Setting MMS Proxy to %s" % (sys.argv[1])
+
+	context.SetProperty("MessageCenter", sys.argv[2])
+	print "Setting MMSC to %s" % (sys.argv[2])
--- test/test-push-notification
+++ test/test-push-notification
+#!/usr/bin/python
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class PushNotificationAgent(dbus.service.Object):
+	@dbus.service.method("org.ofono.PushNotificationAgent",
+					in_signature="", out_signature="")
+	def Release(self):
+		print "Release"
+		mainloop.quit()
+
+	@dbus.service.method("org.ofono.PushNotificationAgent",
+				in_signature="aya{sv}", out_signature="")
+	def ReceiveNotification(self, data, props):
+		for key in props.keys():
+			print "Key: %s, Value: %s" % (key, props[key])
+
+		print "Received notification of size: %d" % len(data)
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+	manager = dbus.Interface(bus.get_object("org.ofono", "/"),
+							"org.ofono.Manager")
+
+	modems = manager.GetModems()
+
+	for path, properties in modems:
+		if "org.ofono.PushNotification" not in properties["Interfaces"]:
+			continue
+
+		pn = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.PushNotification')
+
+	path = "/test/agent"
+	agent = PushNotificationAgent(bus, path)
+	pn.RegisterAgent(path)
+	print "Agent registered"
+
+	mainloop = gobject.MainLoop()
+	mainloop.run()
--- test/test-smart-messaging
+++ test/test-smart-messaging
+#!/usr/bin/python
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class SmartMessagingAgent(dbus.service.Object):
+	@dbus.service.method("org.ofono.SmartMessagingAgent",
+					in_signature="", out_signature="")
+	def Release(self):
+		print "Release"
+		mainloop.quit()
+
+	@dbus.service.method("org.ofono.SmartMessagingAgent",
+				in_signature="aya{sv}", out_signature="")
+	def ReceiveBusinessCard(self, data, props):
+		for key in props.keys():
+			print "Key: %s, Value: %s" % (key, props[key])
+
+		string = ""
+		for byte in data:
+			string += str(byte)
+
+		print "Received Business Card:"
+		print string
+
+	@dbus.service.method("org.ofono.SmartMessagingAgent",
+				in_signature="aya{sv}", out_signature="")
+	def ReceiveAppointment(self, data, props):
+		for key in props.keys():
+			print "Key: %s, Value: %s" % (key, props[key])
+
+		string = ""
+		for byte in data:
+			string += str(byte)
+
+		print "Received Appointment:"
+		print string
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+	manager = dbus.Interface(bus.get_object("org.ofono", "/"),
+							"org.ofono.Manager")
+
+	modems = manager.GetModems()
+
+	for path, properties in modems:
+		if "org.ofono.SmartMessaging" not in properties["Interfaces"]:
+			continue
+
+		pn = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.SmartMessaging')
+
+	path = "/test/agent"
+	agent = SmartMessagingAgent(bus, path)
+	pn.RegisterAgent(path)
+	print "Agent registered"
+
+	mainloop = gobject.MainLoop()
+	mainloop.run()
--- unit/test-sms.c
+++ unit/test-sms.c
@@ -785,7 +785,7 @@
 	if (g_test_verbose())
 		g_printf("Text:\n%s\n", utf8);
 
-	l = sms_text_prepare(utf8, ref, TRUE, NULL, FALSE);
+	l = sms_text_prepare("555", utf8, ref, TRUE, FALSE);
 	g_assert(l);
 	g_assert(g_slist_length(l) == 3);
 
@@ -815,7 +815,8 @@
 	int encoded_tpdu_len;
 	char *encoded_pdu;
 
-	r = sms_text_prepare(test_no_fragmentation_7bit, 0, FALSE, NULL, FALSE);
+	r = sms_text_prepare("555", test_no_fragmentation_7bit, 0,
+				FALSE, FALSE);
 
 	g_assert(r != NULL);
 
@@ -897,7 +898,7 @@
 	if (g_test_verbose())
 		g_print("strlen: %zd\n", strlen(test->str));
 
-	r = sms_text_prepare(test->str, 0, TRUE, NULL, FALSE);
+	r = sms_text_prepare("+15554449999", test->str, 0, TRUE, FALSE);
 	g_assert(r);
 	g_assert(g_slist_length(r) == test->segments);
 
@@ -906,7 +907,6 @@
 
 		sms = l->data;
 
-		sms_address_from_string(&sms->submit.daddr, "+15554449999");
 		sms_encode(sms, &pdu_len, &tpdu_len, pdu);
 		g_assert(pdu_len == (tpdu_len + 1));
 
@@ -973,7 +973,7 @@
 
 	utf8[i] = '\0';
 
-	l = sms_text_prepare(utf8, 0, use_16bit, NULL, FALSE);
+	l = sms_text_prepare("555", utf8, 0, use_16bit, FALSE);
 
 	g_assert(l);
 	g_assert(g_slist_length(l) == 255);
@@ -986,7 +986,7 @@
 	memcpy(utf8 + i, utf8_char, stride);
 	utf8[i+stride] = '\0';
 
-	l = sms_text_prepare(utf8, 0, use_16bit, NULL, FALSE);
+	l = sms_text_prepare("555", utf8, 0, use_16bit, FALSE);
 
 	g_assert(l == NULL);
 	g_free(utf8);

++++++ ofono.yaml
--- ofono.yaml
+++ ofono.yaml
@@ -1,6 +1,6 @@
 Name: ofono
 Summary: Open Source Telephony
-Version: 0.33
+Version: 0.36
 Release: 1
 Group: System/Networking
 License: GPLv2




More information about the MeeGo-commits mailing list