/******************************************************************************* * Software Name : RCS IMS Stack * * Copyright (C) 2010 France Telecom S.A. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.orangelabs.rcs.provisioning.https; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.HttpVersion; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.CookieStore; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.params.CookiePolicy; import org.apache.http.client.params.HttpClientParams; import org.apache.http.client.protocol.ClientContext; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.params.ConnManagerPNames; import org.apache.http.conn.params.ConnPerRouteBean; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.SingleClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Proxy; import android.os.Environment; import android.telephony.TelephonyManager; import android.text.TextUtils; import com.orangelabs.rcs.provider.settings.RcsSettings; import com.orangelabs.rcs.provider.settings.RcsSettingsData; import com.orangelabs.rcs.provisioning.ProvisioningFailureReasons; import com.orangelabs.rcs.provisioning.ProvisioningInfo; import com.orangelabs.rcs.provisioning.ProvisioningInfo.Version; import com.orangelabs.rcs.provisioning.ProvisioningParser; import com.orangelabs.rcs.provisioning.TermsAndConditionsRequest; import com.orangelabs.rcs.service.LauncherUtils; import com.orangelabs.rcs.utils.HttpUtils; import com.orangelabs.rcs.utils.NetworkUtils; import com.orangelabs.rcs.utils.StringUtils; import com.orangelabs.rcs.utils.logger.Logger; /** * Provisioning via network manager * * @author hlxn7157 * @author G. LE PESSOT * @author Deutsche Telekom AG */ public class HttpsProvisioningManager { /** * First launch flag */ private boolean first = false; /** * User action flag */ private boolean user = false; /** * Retry counter */ private int retryCount = 0; /** * Check if a provisioning request is already pending */ private boolean isPending = false; /** * The Service Context */ private Context context; /** * Provisioning SMS manager */ HttpsProvisioningSMS smsManager; /** * Provisioning Connection manager */ HttpsProvisioningConnection networkConnection; /** * Retry after 511 "Network authentication required" counter */ private int retryAfter511ErrorCount = 0; /** * Retry intent */ private PendingIntent retryIntent; /** * The logger */ private Logger logger = Logger.getLogger(this.getClass().getName()); /** * Constructor * * @param applicationContext * @param retryIntent * pending intent to update periodically the configuration * @param first * is provisioning service launch after (re)boot ? * @param user * is provisioning service launch after user action ? */ public HttpsProvisioningManager(Context applicationContext, final PendingIntent retryIntent, boolean first, boolean user) { this.context = applicationContext; this.retryIntent = retryIntent; this.first = first; this.user = user; this.smsManager = new HttpsProvisioningSMS(this); this.networkConnection = new HttpsProvisioningConnection(this); } /** * @return the context */ protected Context getContext() { return context; } /** * Connection event * * @param action Connectivity action * @return true if the updateConfig has been done */ protected boolean connectionEvent(String action) { if (!isPending) { if (logger.isActivated()) { logger.debug("Connection event " + action); } if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { // Check received network info NetworkInfo networkInfo = networkConnection.getConnectionMngr().getActiveNetworkInfo(); if ((networkInfo != null) && networkInfo.isConnected()) { isPending = true; if (logger.isActivated()) { logger.debug("Connected to data network"); } Thread t = new Thread() { public void run() { updateConfig(); } }; t.start(); // Unregister network state listener networkConnection.unregisterNetworkStateListener(); isPending = false; return true; } } } return false; } /** * Execute an HTTP request * * @param protocol HTTP protocol * @param request HTTP request * @return HTTP response * @throws URISyntaxException * @throws IOException * @throws ClientProtocolException */ protected HttpResponse executeRequest(String protocol, String request, DefaultHttpClient client, HttpContext localContext) throws URISyntaxException, ClientProtocolException, IOException { try { HttpGet get = new HttpGet(); get.setURI(new URI(protocol + "://" + request)); get.addHeader("Accept-Language", HttpsProvisioningUtils.getUserLanguage()); if (logger.isActivated()) { logger.debug("HTTP request: " + get.getURI().toString()); } HttpResponse response = client.execute(get, localContext); if (logger.isActivated()) { logger.debug("HTTP response: " + response.getStatusLine().toString()); } return response; } catch (UnknownHostException e) { if (logger.isActivated()) { logger.debug("The server " + request + " can't be reached!"); } return null; } } /** * Get the HTTPS request arguments * * @param imsi Imsi * @param imei Imei * @return {@link String} with the HTTPS request arguments. */ protected String getHttpsRequestArguments(String imsi, String imei) { return getHttpsRequestArguments(imsi, imei, null, null, null); } /** * Get the HTTPS request arguments * * @param imsi Imsi * @param imei Imei * @param smsPort SMS port * @param token Provisioning token * @param msisdn MSISDN * @return {@link String} with the HTTPS request arguments. */ private String getHttpsRequestArguments(String imsi, String imei, String smsPort, String token, String msisdn) { String vers = RcsSettings.getInstance().getProvisioningVersion(); if (this.user && ProvisioningInfo.Version.DISABLED_DORMANT.equals(vers)) { vers = LauncherUtils.getProvisioningVersion(context); this.user = false; } String args = "?vers=" + vers + "&rcs_version=" + HttpsProvisioningUtils.getRcsVersion() + "&rcs_profile=" + HttpsProvisioningUtils.getRcsProfile() + "&client_vendor=" + HttpsProvisioningUtils.getClientVendorFromContext(context) + "&client_version=" + HttpsProvisioningUtils.getClientVersionFromContext(context) + "&terminal_vendor=" + HttpUtils.encodeURL(HttpsProvisioningUtils.getTerminalVendor()) + "&terminal_model=" + HttpUtils.encodeURL(HttpsProvisioningUtils.getTerminalModel()) + "&terminal_sw_version=" + HttpUtils.encodeURL(HttpsProvisioningUtils.getTerminalSoftwareVersion()); if (imsi != null) { // Add optional parameter IMSI only if available args += "&IMSI=" + imsi; } if (imei != null) { // Add optional parameter IMEI only if available args += "&IMEI=" + imei; } if (smsPort != null) { // Add SMS port if available args += "&SMS_port=" + smsPort; } if (msisdn != null) { // Add token if available args += "&msisdn=" + msisdn; } if (token != null) { // Add token if available args += "&token=" + token; } return args; } /** * Send the first HTTPS request to require the one time password (OTP) * * @param imsi IMSI * @param imei IMEI * @param requestUri Request URI * @param client Instance of {@link DefaultHttpClient} * @param localContext Instance of {@link HttpContext} * @return Instance of {@link HttpsProvisioningResult} or null in case of internal exception */ protected HttpsProvisioningResult sendFirstRequestsToRequireOTP(String imsi, String imei, String msisdn, String primaryUri, String secondaryUri, DefaultHttpClient client, HttpContext localContext) { HttpsProvisioningResult result = new HttpsProvisioningResult(); try { if (logger.isActivated()) { logger.debug("HTTP provisioning - Send first HTTPS request to require OTP"); } // Generate the SMS port for provisioning String smsPortForOTP = HttpsProvisioningSMS.generateSmsPortForProvisioning(); // Format first HTTPS request with extra parameters (IMSI and IMEI if available plus SMS_port and token) String token = (!TextUtils.isEmpty(RcsSettings.getInstance().getProvisioningToken()) ? RcsSettings.getInstance().getProvisioningToken() : ""); String args = getHttpsRequestArguments(imsi, imei, smsPortForOTP, token, msisdn); // Execute first HTTPS request with extra parameters String request = primaryUri + args; HttpResponse response = executeRequest("https", request, client, localContext); if (response == null && !StringUtils.isEmpty(secondaryUri)) { // First server not available, try the secondaryUri request = secondaryUri + args; response = executeRequest("https", request, client, localContext); } if (response == null) { return null; } result.code = response.getStatusLine().getStatusCode(); result.content = new String(EntityUtils.toByteArray(response.getEntity()), "UTF-8"); if (result.code != 200) { if (result.code == 403) { if (logger.isActivated()) { logger.debug("First HTTPS request to require OTP failed: Forbidden (request status code: 403) for msisdn "+msisdn); } msisdn = RcsSettings.getInstance().getMsisdn(); msisdn = HttpsProvionningMSISDNInput.getInstance().displayPopupAndWaitResponse(context); if (msisdn == null) { return null; } else { return sendFirstRequestsToRequireOTP(imsi, imei, msisdn, primaryUri, secondaryUri, client, localContext); } } else if (result.code == 503) { if (logger.isActivated()) { logger.debug("First HTTPS request to require OTP failed: Retry After (request status code: 503)"); } result.retryAfter = getRetryAfter(response); } else if (result.code == 511) { if (logger.isActivated()) { logger.debug("First HTTPS request to require OTP failed: Invalid token (request status code: 511)"); } } } else { if (logger.isActivated()) { logger.debug("HTTPS request returns with 200 OK."); } // Register SMS provisioning receiver smsManager.registerSmsProvisioningReceiver(smsPortForOTP, primaryUri, client, localContext); // Save the MSISDN RcsSettings.getInstance().setMsisdn(msisdn); // If the content is empty, means that the configuration XML is not present // and the Token is invalid then we need to wait for the SMS with OTP if (TextUtils.isEmpty(result.content)) { // Wait for SMS OTP result.waitingForSMSOTP = true; } } // If not waiting for the sms with OTP if (!result.waitingForSMSOTP) { // Unregister SMS provisioning receiver smsManager.unregisterSmsProvisioningReceiver(); } return result; } catch (UnknownHostException e) { if (logger.isActivated()) { logger.warn("First HTTPS request to require OTP failed: Provisioning server not reachable"); } return null; } catch (Exception e) { if (logger.isActivated()) { logger.error("First HTTPS request to require OTP failed: Can't get config via HTTPS", e); } return null; } } /** * Update provisioning config with OTP * * @param otp One time password * @param requestUri Request URI * @param client Instance of {@link DefaultHttpClient} * @param localContext Instance of {@link HttpContext} */ protected void updateConfigWithOTP(String otp, String requestUri, DefaultHttpClient client, HttpContext localContext) { // Cancel previous retry alarm HttpsProvisioningService.cancelRetryAlarm(context, retryIntent); // Get config via HTTPS with OTP HttpsProvisioningResult result = sendSecondHttpsRequestWithOTP(otp, requestUri, client, localContext); // Process HTTPS provisioning result processProvisioningResult(result); } /** * Build the provisioning address with SIM information * * @return provisioning URI */ private String buildProvisioningAddress(TelephonyManager tm) { // Get SIM info String ope = tm.getSimOperator(); if (ope == null || ope.length() < 4) { if (logger.isActivated()) { logger.warn("Can not read network operator from SIM card!"); } return null; } String mnc = ope.substring(3); String mcc = ope.substring(0, 3); while (mnc.length() < 3) { // Set mnc on 3 digits mnc = "0" + mnc; } return "config.rcs." + "mnc" + mnc + ".mcc" + mcc + ".pub.3gppnetwork.org"; } /** * Get configuration * * @return Result or null in case of internal exception */ private HttpsProvisioningResult getConfig() { HttpsProvisioningResult result = new HttpsProvisioningResult(); try { if (logger.isActivated()) { logger.debug("Request config via HTTPS"); } // Get provisioning address TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); String primaryUri = null; String secondaryUri = null; if (RcsSettings.getInstance().isSecondaryProvisioningAddressOnly()) { primaryUri = RcsSettings.getInstance().getSecondaryProvisioningAddress(); } else { primaryUri = buildProvisioningAddress(tm); secondaryUri = RcsSettings.getInstance().getSecondaryProvisioningAddress(); } // Check if a configuration file for HTTPS provisioning exists String PROVISIONING_FILE = Environment.getExternalStorageDirectory().getPath() + "/joyn_provisioning.txt"; try { File file = new File(PROVISIONING_FILE); if (file.exists()) { if (logger.isActivated()) { logger.debug("Provisioning file found !"); } FileInputStream fis = new FileInputStream(PROVISIONING_FILE); DataInputStream in = new DataInputStream(fis); BufferedReader br = new BufferedReader(new InputStreamReader(in)); primaryUri = br.readLine(); secondaryUri = null; in.close(); } } catch (Exception e) { // Nothing to do } if (logger.isActivated()) { logger.debug("HCS/RCS Uri to connect: "+ primaryUri + " or " + secondaryUri); } String imsi = tm.getSubscriberId(); String imei = tm.getDeviceId(); tm = null; // Format HTTP request SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443)); HttpParams params = new BasicHttpParams(); params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30); params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30)); params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false); NetworkInfo networkInfo = networkConnection.getConnectionMngr().getActiveNetworkInfo(); if (networkInfo != null) { String proxyHost = Proxy.getDefaultHost(); if (proxyHost != null && proxyHost.length() > 1) { int proxyPort = Proxy.getDefaultPort(); params.setParameter(ConnRoutePNames.DEFAULT_PROXY, new HttpHost(proxyHost, proxyPort)); } } HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); // Support broad variety of different cookie types (not just Netscape but RFC 2109 and RFC2965 compliant ones, too) HttpClientParams.setCookiePolicy(params, CookiePolicy.BROWSER_COMPATIBILITY); ClientConnectionManager cm = new SingleClientConnManager(params, schemeRegistry); DefaultHttpClient client = new DefaultHttpClient(cm, params); CookieStore cookieStore = (CookieStore) new BasicCookieStore(); HttpContext localContext = new BasicHttpContext(); localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore); // If network is not mobile network, use request with OTP if (networkInfo != null && networkInfo.getType() != ConnectivityManager.TYPE_MOBILE) { // Proceed with non mobile network registration return sendFirstRequestsToRequireOTP(imsi, imei, null, primaryUri, secondaryUri, client, localContext); } if (logger.isActivated()) { logger.debug("HTTP provisioning on mobile network"); } // Execute first HTTP request String requestUri = primaryUri; HttpResponse response = executeRequest("http", requestUri, client, localContext); if (response == null && !StringUtils.isEmpty(secondaryUri)) { // First server not available, try the secondaryUri requestUri = secondaryUri; response = executeRequest("http", requestUri, client, localContext); } if (response == null) { return null; } result.code = response.getStatusLine().getStatusCode(); result.content = new String(EntityUtils.toByteArray(response.getEntity()), "UTF-8"); if (result.code == 511) { // Blackbird guidelines ID_2_6 Configuration mechanism over PS without Header Enrichment // Use SMS provisionning on PS data network if server reply 511 NETWORK AUTHENTICATION REQUIRED return sendFirstRequestsToRequireOTP(imsi, imei, null, primaryUri, secondaryUri, client, localContext); } else if (result.code != 200) { if (result.code == 503) { result.retryAfter = getRetryAfter(response); } return result; } // Format second HTTPS request String args = getHttpsRequestArguments(imsi, imei); String request = requestUri + args; if (logger.isActivated()) { logger.info("Request provisioning: "+ request); } // Execute second HTTPS request response = executeRequest("https", request, client, localContext); if (response == null) { return null; } result.code = response.getStatusLine().getStatusCode(); if (result.code != 200) { if (result.code == 503) { result.retryAfter = getRetryAfter(response); } return result; } result.content = new String(EntityUtils.toByteArray(response.getEntity()), "UTF-8"); return result; } catch(UnknownHostException e) { if (logger.isActivated()) { logger.warn("Provisioning server not reachable"); } return null; } catch(Exception e) { if (logger.isActivated()) { logger.error("Can't get config via HTTPS", e); } return null; } } /** * Update provisioning config */ protected void updateConfig() { // Cancel previous retry alarm HttpsProvisioningService.cancelRetryAlarm(context, retryIntent); // Get config via HTTPS HttpsProvisioningResult result = getConfig(); // Process HTTPS provisioning result processProvisioningResult(result); } /** * Send the second HTTPS request with the one time password (OTP) * * @param otp One time password * @param requestUri Request URI * @param client Instance of {@link DefaultHttpClient} * @param localContext Instance of {@link HttpContext} * @return Instance of {@link HttpsProvisioningResult} or null in case of internal exception */ protected HttpsProvisioningResult sendSecondHttpsRequestWithOTP(String otp, String requestUri, DefaultHttpClient client, HttpContext localContext) { HttpsProvisioningResult result = new HttpsProvisioningResult(); try { if (logger.isActivated()) { logger.debug("Send second HTTPS with OTP"); } // Format second HTTPS request String args = "?OTP=" + otp; String request = requestUri + args; if (logger.isActivated()) { logger.info("Request provisioning with OTP: " + request); } // Execute second HTTPS request HttpResponse response = executeRequest("https", request, client, localContext); if (response == null) { return null; } result.code = response.getStatusLine().getStatusCode(); if (result.code != 200) { if (result.code == 503) { result.retryAfter = getRetryAfter(response); } else if (result.code == 511) { if (logger.isActivated()) { logger.debug("Second HTTPS request with OTP failed: Invalid one time password (request status code: 511)"); } } return result; } result.content = new String(EntityUtils.toByteArray(response.getEntity()), "UTF-8"); return result; } catch (Exception e) { if (logger.isActivated()) { logger.error("Second HTTPS request with OTP failed: Can't get config via HTTPS", e); } return null; } } /** * Get retry-after value * * @return retry-after value */ protected int getRetryAfter(HttpResponse response) { Header[] headers = response.getHeaders("Retry-After"); if (headers.length > 0) { try { return Integer.parseInt(headers[0].getValue()); } catch (NumberFormatException e) { return 0; } } return 0; } /** * Process provisioning result * * @param result Instance of {@link HttpsProvisioningResult} */ private void processProvisioningResult(HttpsProvisioningResult result) { if (result != null) { if (result.code == 200) { // Reset after 511 counter retryAfter511ErrorCount = 0; if (result.waitingForSMSOTP) { if (logger.isActivated()) { logger.debug("Waiting for SMS with OTP."); } return; } if (logger.isActivated()) { logger.debug("Provisioning request successful"); } // Parse the received content ProvisioningParser parser = new ProvisioningParser(result.content); // Save GSMA release set into the provider int gsmaRelease = RcsSettings.getInstance().getGsmaRelease(); // Before parsing the provisioning, the GSMA release is set to Albatros RcsSettings.getInstance().setGsmaRelease(RcsSettingsData.VALUE_GSMA_REL_ALBATROS); if (parser.parse(gsmaRelease)) { // Successfully provisioned, 1st time reg finalized first = false; ProvisioningInfo info = parser.getProvisioningInfo(); // Save version String version = info.getVersion(); long validity = info.getValidity(); if (logger.isActivated()) { logger.debug("Provisioning version=" + version + ", validity=" + validity); } // Save the latest positive version of the configuration LauncherUtils.saveProvisioningVersion(context, version); // Save the validity of the configuration LauncherUtils.saveProvisioningValidity(context, validity); RcsSettings.getInstance().setProvisioningVersion(version); // Save token String token = info.getToken(); long tokenValidity = info.getTokenValidity(); if (logger.isActivated()) logger.debug("Provisioning Token=" + token + ", validity=" + tokenValidity); RcsSettings.getInstance().setProvisioningToken(token); // Reset retry alarm counter retryCount = 0; if (ProvisioningInfo.Version.DISABLED_DORMANT.equals(version)) { // -3 : Put RCS client in dormant state if (logger.isActivated()) logger.debug("Provisioning: RCS client in dormant state"); // Start retry alarm if (validity > 0) { HttpsProvisioningService.startRetryAlarm(context, retryIntent, validity * 1000); } // Stop the RCS core service. Provisioning is still running. LauncherUtils.stopRcsCoreService(context); } else if (ProvisioningInfo.Version.DISABLED_NOQUERY.equals(version)) { // -2 : Disable RCS client and stop configuration query if (logger.isActivated()) logger.debug("Provisioning: disable RCS client"); // Disable and stop RCS service RcsSettings.getInstance().setServiceActivationState(false); LauncherUtils.stopRcsService(context); } else if (ProvisioningInfo.Version.RESETED_NOQUERY.equals(version)) { // -1 Forbidden: reset account + version = 0-1 (doesn't restart) if (logger.isActivated()) logger.debug("Provisioning forbidden: reset account"); // Reset config LauncherUtils.resetRcsConfig(context); // Force version to "-1" (resetRcs set version to "0") RcsSettings.getInstance().setProvisioningVersion(version); // Disable the RCS service RcsSettings.getInstance().setServiceActivationState(false); } else if (ProvisioningInfo.Version.RESETED.equals(version)) { if (logger.isActivated()) logger.debug("Provisioning forbidden: no account"); // Reset config LauncherUtils.resetRcsConfig(context); } else { // Start retry alarm if (validity > 0) { HttpsProvisioningService.startRetryAlarm(context, retryIntent, validity * 1000); } // Terms request if (info.getMessage() != null && !RcsSettings.getInstance().isProvisioningTermsAccepted()) { showTermsAndConditions(info); } // Start the RCS core service LauncherUtils.launchRcsCoreService(context); } } else { if (logger.isActivated()) logger.debug("Can't parse provisioning document"); // Restore GSMA release saved before parsing of the provisioning RcsSettings.getInstance().setGsmaRelease("" + gsmaRelease); if (first) { if (logger.isActivated()) logger.debug("As this is first launch and we do not have a valid configuration yet, retry later"); // Reason: Invalid configuration provisioningFails(ProvisioningFailureReasons.INVALID_CONFIGURATION); retry(); } else { if (logger.isActivated()) logger.debug("This is not first launch, use old configuration to register"); tryLaunchRcsCoreService(context, -1); } } } else if (result.code == 503) { // Server Unavailable if (logger.isActivated()) logger.debug("Server Unavailable. Retry after: " + result.retryAfter); if (first) { // Reason: Unable to get configuration provisioningFails(ProvisioningFailureReasons.UNABLE_TO_GET_CONFIGURATION); if (result.retryAfter > 0) { HttpsProvisioningService.startRetryAlarm(context, retryIntent, result.retryAfter * 1000); } } else { tryLaunchRcsCoreService(context, result.retryAfter * 1000); } } else if (result.code == 403) { // Forbidden: reset account + version = 0 if (logger.isActivated()) logger.debug("Provisioning forbidden: reset account"); // Reset version to "0" RcsSettings.getInstance().setProvisioningVersion(Version.RESETED.toString()); // Reset config LauncherUtils.resetRcsConfig(context); // Reason: Provisioning forbidden provisioningFails(ProvisioningFailureReasons.PROVISIONING_FORBIDDEN); } else if (result.code == 511) { // Provisioning authentication required if (logger.isActivated()) logger.debug("Provisioning authentication required"); // Reset provisioning token RcsSettings.getInstance().setProvisioningToken(""); // Retry after reseting provisioning token if (!retryAfter511Error()) { // Reason: Provisioning authentication required provisioningFails(ProvisioningFailureReasons.PROVISIONING_AUTHENTICATION_REQUIRED); } } else { // Other error if (logger.isActivated()) logger.debug("Provisioning error " + result.code); // Start the RCS service if (first) { // Reason: No configuration present provisioningFails(ProvisioningFailureReasons.CONNECTIVITY_ISSUE); retry(); } else { tryLaunchRcsCoreService(context, -1); } } } else { // result is null // Start the RCS service if (first) { // Reason: No configuration present if (logger.isActivated()) logger.error("### Provisioning fails and first = true!"); provisioningFails(ProvisioningFailureReasons.CONNECTIVITY_ISSUE); retry(); } else { tryLaunchRcsCoreService(context, -1); } } } /** * Try to launch RCS Core Service. RCS Service is only launched if version is positive. * * @param context * @param timerRetry * timer to trigger next provisioning request. Only applicable if greater than 0. */ private void tryLaunchRcsCoreService(Context context, int timerRetry) { try { int version = Integer.parseInt(RcsSettings.getInstance().getProvisioningVersion()); // Only launch service if version is positive if (version > 0) { // Start the RCS service LauncherUtils.launchRcsCoreService(context); if (timerRetry > 0) { HttpsProvisioningService.startRetryAlarm(context, retryIntent, timerRetry); } else retry(); } else { // Only retry provisioning if service is disabled dormant (-3) if (ProvisioningInfo.Version.DISABLED_DORMANT.getVersion() == version) { if (timerRetry > 0) { HttpsProvisioningService.startRetryAlarm(context, retryIntent, timerRetry); } else retry(); } } } catch (NumberFormatException e) { } } /** * Show the terms and conditions request * * @param info Provisioning info */ private void showTermsAndConditions(ProvisioningInfo info) { final Intent intent = new Intent(Intent.ACTION_VIEW); intent.setClass(context, TermsAndConditionsRequest.class); // Required as the activity is started outside of an Activity context intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); // Add intent parameters intent.putExtra(TermsAndConditionsRequest.ACCEPT_BTN_KEY, info.getAcceptBtn()); intent.putExtra(TermsAndConditionsRequest.REJECT_BTN_KEY, info.getRejectBtn()); intent.putExtra(TermsAndConditionsRequest.TITLE_KEY, info.getTitle()); intent.putExtra(TermsAndConditionsRequest.MESSAGE_KEY, info.getMessage()); context.startActivity(intent); } /** * Retry after 511 "Network authentication required" procedure * * @return <code>true</code> if retry is performed, otherwise <code>false</code> */ private boolean retryAfter511Error() { if (retryAfter511ErrorCount < HttpsProvisioningUtils.RETRY_AFTER_511_ERROR_MAX_COUNT) { retryAfter511ErrorCount++; HttpsProvisioningService.startRetryAlarm(context, retryIntent, HttpsProvisioningUtils.RETRY_AFTER_511_ERROR_TIMEOUT); if (logger.isActivated()) { logger.debug("Retry after 511 error (" + retryAfter511ErrorCount + "/" + HttpsProvisioningUtils.RETRY_AFTER_511_ERROR_MAX_COUNT + ") provisionning after " + HttpsProvisioningUtils.RETRY_AFTER_511_ERROR_TIMEOUT + "ms"); } return true; } if (logger.isActivated()) { logger.debug("No more retry after 511 error for provisionning"); } // Reset after 511 counter retryAfter511ErrorCount = 0; return false; } /** * Provisioning fails. * * @param reason Reason of failure */ public void provisioningFails(int reason) { // If wifi is active network access type if (NetworkUtils.getNetworkAccessType() == NetworkUtils.NETWORK_ACCESS_WIFI) { // Register Wifi disabling listener networkConnection.registerWifiDisablingListener(); } } /** * Retry procedure */ private void retry() { if (retryCount < HttpsProvisioningUtils.RETRY_MAX_COUNT) { retryCount++; int retryDelay = HttpsProvisioningUtils.RETRY_BASE_TIMEOUT + 2 * (retryCount - 1) * HttpsProvisioningUtils.RETRY_BASE_TIMEOUT; HttpsProvisioningService.startRetryAlarm(context, retryIntent, retryDelay); if (logger.isActivated()) { logger.debug("Retry provisionning count: "+retryCount ); } } else { if (logger.isActivated()) { logger.debug("No more retry for provisionning"); } } } /** * Transmit to SMS unregister method */ public void unregisterSmsProvisioningReceiver() { smsManager.unregisterSmsProvisioningReceiver(); } /** * Transmit to Network unregister method */ public void unregisterNetworkStateListener() { networkConnection.unregisterNetworkStateListener(); } /** * Transmit to Network unregister wifi method */ public void unregisterWifiDisablingListener() { networkConnection.unregisterWifiDisablingListener(); } /** * Transmit to Network register method */ public void registerNetworkStateListener() { networkConnection.registerNetworkStateListener(); } /** * Retry procedure */ public void resetCounters() { // Reset retry alarm counter retryCount = 0; // Reset after 511 counter retryAfter511ErrorCount = 0; } }