100 | | == Symbian specific issues == #sym |
101 | | |
102 | | === Socket call get stuck after reconnecting to different access point (AP) === #symstk1 |
103 | | |
104 | | '''Symptom:''' :: |
105 | | The library gets stuck in {{{pjsua_destroy()}}} when the application tries to destroy the stack when it detects that the AP connection is down. |
106 | | |
107 | | This is a problem with the socket in general and not PJSIP. Below are steps to reproduce with a plain UDP socket: |
108 | | 1. Create RConnection, call Start() to connect to AP |
109 | | 2. Create UDP socket, call !SendTo() to send a packet |
110 | | 3. Disconnect the AP (Menu -> Connectivity -> Conn. mgr. -> Active data connections -> (highlight the AP) -> Options (menu) -> Disconnect). |
111 | | 4. Call udp.!SendTo() again |
112 | | 5. AP selection dialog appears, select different AP |
113 | | 6. !WaitForRequest() to the udp.!SendTo() operation now '''will get stuck for 1-2 minutes''' before ''tcpip6_error_NoRoute'' error (-5105) is returned. |
114 | | 7. Now if you call udp.!SendTo() again, now !WaitForRequest() will get stuck indefinitely |
115 | | |
116 | | The problem above does not occur if: |
117 | | - the user selects the same AP. In this case the udp.!SendTo() should complete successfully |
118 | | - the user cancels the AP selection dialog. In this case the udp.!SendTo() will fail immediately with KErrCancel without blocking the application. |
119 | | - the socket is closed and re-opened. In this case the udp.!SendTo() should complete successfully |
120 | | |
121 | | As additional info: |
122 | | - the problem still persists even if the RConnection is restarted (RConnection.Start() is called to select new AP) in between step 3 and 4 above. |
123 | | |
124 | | |
125 | | Currently we don't have a solution for this, except to only use one AP (perhaps to select the AP when PJSIP is started and lock the RConnection that is assigned to PJLIB to this AP). |
126 | | |
| 100 | == Symbian specific solution == #sym |
| 101 | |
| 102 | In Symbian, the {{{RConnection.ProgressNotification()}}} method can be used to register an Active Object to be run when the connection status has changed. Below is a sample code taken from {{{symbian_ua\ua.cpp}}} file to restart PJSIP when the connection is down. |
| 103 | |
| 104 | {{{ |
| 105 | class CConnMon : public CActive { |
| 106 | public: |
| 107 | static CConnMon* NewL(RConnection &conn, RSocketServ &sserver) { |
| 108 | CConnMon *self = new (ELeave) CConnMon(conn, sserver); |
| 109 | CleanupStack::PushL(self); |
| 110 | self->ConstructL(); |
| 111 | CleanupStack::Pop(self); |
| 112 | return self; |
| 113 | } |
| 114 | |
| 115 | void Start() { |
| 116 | conn_.ProgressNotification(nif_progress_, iStatus); |
| 117 | SetActive(); |
| 118 | } |
| 119 | |
| 120 | void Stop() { |
| 121 | Cancel(); |
| 122 | } |
| 123 | |
| 124 | ~CConnMon() { Stop(); } |
| 125 | |
| 126 | private: |
| 127 | CConnMon(RConnection &conn, RSocketServ &sserver) : |
| 128 | CActive(EPriorityHigh), |
| 129 | conn_(conn), |
| 130 | sserver_(sserver) |
| 131 | { |
| 132 | CActiveScheduler::Add(this); |
| 133 | } |
| 134 | |
| 135 | void ConstructL() {} |
| 136 | |
| 137 | void DoCancel() { |
| 138 | conn_.CancelProgressNotification(); |
| 139 | } |
| 140 | |
| 141 | void RunL() { |
| 142 | if (nif_progress_().iStage == KLinkLayerClosed) { |
| 143 | pj_status_t status; |
| 144 | TInt err; |
| 145 | |
| 146 | // Tell pjlib the connection has been down. |
| 147 | pj_symbianos_set_connection_status(PJ_FALSE); |
| 148 | |
| 149 | PJ_LOG(3, (THIS_FILE, "RConnection closed, restarting PJSUA..")); |
| 150 | |
| 151 | // Destroy pjsua |
| 152 | pjsua_destroy(); |
| 153 | PJ_LOG(3, (THIS_FILE, "PJSUA destroyed.")); |
| 154 | |
| 155 | // Reopen the connection |
| 156 | err = conn_.Open(sserver_); |
| 157 | if (err == KErrNone) |
| 158 | err = conn_.Start(); |
| 159 | if (err != KErrNone) { |
| 160 | CActiveScheduler::Stop(); |
| 161 | return; |
| 162 | } |
| 163 | |
| 164 | // Reinit Symbian OS param before pj_init() |
| 165 | pj_symbianos_params sym_params; |
| 166 | pj_bzero(&sym_params, sizeof(sym_params)); |
| 167 | sym_params.rsocketserv = &sserver_; |
| 168 | sym_params.rconnection = &conn_; |
| 169 | pj_symbianos_set_params(&sym_params); |
| 170 | |
| 171 | // Reinit pjsua |
| 172 | status = app_startup(); |
| 173 | if (status != PJ_SUCCESS) { |
| 174 | pjsua_perror(THIS_FILE, "app_startup() error", status); |
| 175 | CActiveScheduler::Stop(); |
| 176 | return; |
| 177 | } |
| 178 | |
| 179 | |
| 180 | PJ_LOG(3, (THIS_FILE, "PJSUA restarted.")); |
| 181 | PrintMenu(); |
| 182 | } |
| 183 | |
| 184 | Start(); |
| 185 | } |
| 186 | |
| 187 | private: |
| 188 | RConnection& conn_; |
| 189 | RSocketServ& sserver_; |
| 190 | TNifProgressBuf nif_progress_; |
| 191 | }; |
| 192 | |
| 193 | What the code in {{{RunL()}}} above does is it shuts down PJSIP when the connection is down, ask user to reconnect by showing up the access point dialog, and (re)start the application. |
| 194 | |
| 195 | The '''{{{pj_symbianos_set_connection_status()}}}''' API was added in PJSIP version 1.0.2/1.1. This function is used to tell PJLIB that it should not access any socket calls anymore since the connection has been down. Without this function, Symbian will pop up the access point selection dialog again in {{{pjsua_destroy()}}}, and if the user selects different access point then it will cause the socket to block indefinitely in {{{WaitForRequest()}}}. |
| 196 | |
| 197 | }}} |