1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.omadm.service; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.SharedPreferences; 23 import android.net.ConnectivityManager; 24 import android.net.NetworkInfo; 25 import android.net.wifi.WifiInfo; 26 import android.net.wifi.WifiManager; 27 import android.os.Bundle; 28 import android.os.SystemProperties; 29 import android.telephony.TelephonyManager; 30 import android.text.TextUtils; 31 import android.util.Log; 32 33 import com.android.internal.telephony.Phone; 34 import com.android.internal.telephony.TelephonyProperties; 35 36 import java.io.File; 37 import java.io.FileInputStream; 38 import java.io.FileOutputStream; 39 import java.io.IOException; 40 import java.nio.charset.StandardCharsets; 41 import java.text.SimpleDateFormat; 42 import java.util.Date; 43 import java.util.Locale; 44 import java.util.TimeZone; 45 46 public class DMIntentReceiver extends BroadcastReceiver { 47 private static final String TAG = "DMIntentReceiver"; 48 private static final boolean DBG = DMClientService.DBG; 49 50 private int mUIMode = -1; 51 52 private byte[] mData; 53 54 private static final String ALERT_TYPE_DOWNLOADANDUPDATE 55 = "org.openmobilealliance.dm.firmwareupdate.downloadandupdate"; 56 57 private static final String RP_OPERATIONS_FACTORYRESET 58 = "./ManagedObjects/LAWMO/Operations/FactoryReset"; 59 60 private static final String RP_EXT_OPERATIONS_RESET 61 = "./ManagedObjects/LAWMO/Ext/Operations/Reset"; 62 63 private static final String ACTION_NOTIFY_RESULT_TO_SERVER 64 = "com.android.omadm.service.notify_result_to_server"; 65 66 private static final String ACTION_NOTIFY_START_UP_DMSERVICE 67 = "com.android.omadm.service.start_up"; 68 69 private static final String DEV_DETAIL = "devdetail"; 70 71 private static final String WIFI_MAC_ADDR = "wifimacaddr"; 72 73 private static final String PRE_FW_VER = "prefwversion"; 74 75 private static final String CURR_FW_VER = "currfwversion"; 76 77 private static final String LAST_UPD_TIME = "lastupdatetime"; 78 79 private static boolean initialWapPending; 80 81 @Override 82 public void onReceive(Context context, Intent intent) { 83 if (DMHelper.disableIfSecondaryUser(context)) { 84 return; 85 } 86 87 String action = intent.getAction(); 88 89 logd("Received new intent: " + action); 90 91 if (action.equals(DMIntent.ACTION_WAP_PUSH_RECEIVED_INTERNAL)) { 92 handleWapPushIntent(context, intent); 93 } else if (action.equals(DMIntent.ACTION_CLOSE_NOTIFICATION_INFO)) { 94 DMHelper.cancelNotification(context, DMHelper.NOTIFICATION_INFORMATIVE_ID); 95 } else if (action.equals(DMIntent.ACTION_USER_CONFIRMED_DM_SESSION)) { 96 handleUserConfirmedSession(context); 97 } else if (action.equals(DMIntent.ACTION_CLIENT_INITIATED_FOTA_SESSION)) { 98 handleClientInitiatedFotaIntent(context, intent); 99 } else if (action.equals(DMIntent.ACTION_TIMER_ALERT)) { 100 handleTimeAlertIntent(context); 101 } else if (action.equals(DMIntent.DM_SERVICE_RESULT_INTENT)) { 102 handleDmServiceResult(context, intent); 103 } else if (action.equals(ACTION_NOTIFY_RESULT_TO_SERVER)) { 104 // FIXME old comment: change this to the DMIntent name 105 handleNotifyResultToServer(context, intent); 106 } else if (action.equals(DMIntent.ACTION_DATA_CONNECTION_READY)) { 107 handleDataConnectionReady(context); 108 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 109 logd("Ignoring Intent.ACTION_BOOT_COMPLETED"); 110 //if (!(isPhoneTypeLTE() || isPhoneTypeCDMA3G(context))) { 111 // saveDevDetail(context); 112 // handleBootCompletedIntent(context); 113 //} 114 } else if (action.equals(ACTION_NOTIFY_START_UP_DMSERVICE)) { 115 if (isPhoneTypeLTE()) { 116 saveDevDetail(context); 117 SharedPreferences p = context.getSharedPreferences(DMHelper.IMEI_PREFERENCE_KEY, 0); 118 String currGsmImei = p.getString(DMHelper.IMEI_VALUE_KEY, ""); 119 if (currGsmImei != null && currGsmImei.equals(intent.getStringExtra("gsmimei"))) { 120 logd("IMEI already stored, continuing"); 121 } else { 122 SharedPreferences.Editor ed = p.edit(); 123 ed.putString(DMHelper.IMEI_VALUE_KEY, intent.getStringExtra("gsmimei")); 124 ed.apply(); 125 } 126 } else if (isPhoneTypeCDMA3G(context)) { 127 SharedPreferences p = context.getSharedPreferences(DMHelper.AKEY_PREFERENCE_KEY, 0); 128 SharedPreferences.Editor ed = p.edit(); 129 ed.putString(DMHelper.AKEY_VALUE_KEY, intent.getStringExtra("akey")); 130 ed.apply(); 131 } 132 handleBootCompletedIntent(context); 133 } else if (action.equals(DMIntent.ACTION_INJECT_PACKAGE_0_INTERNAL)) { 134 String strServerID = intent.getStringExtra(DMIntent.FIELD_SERVERID); 135 if (strServerID == null || strServerID.trim().isEmpty()) { 136 logd("Error! Can't inject package0. The required extras parameter '" + 137 DMIntent.FIELD_SERVERID + "' is null or an empty string."); 138 return; 139 } 140 141 Intent newIntent = new Intent(DMIntent.LAUNCH_INTENT); 142 newIntent.putExtra(DMIntent.FIELD_REQUEST_ID, System.currentTimeMillis()); 143 newIntent.putExtra(DMIntent.FIELD_TYPE, DMIntent.TYPE_CLIENT_SESSION_REQUEST); 144 newIntent.putExtra(DMIntent.FIELD_SERVERID, strServerID); 145 logd("XXX received ACTION_INJECT_PACKAGE_0_INTERNAL, starting" 146 + " TYPE_CLIENT_SESSION_REQUEST with ID " 147 + newIntent.getLongExtra(DMIntent.FIELD_REQUEST_ID, 1234)); 148 newIntent.setClass(context, DMClientService.class); 149 context.startService(newIntent); 150 } else if (action.equals(DMIntent.ACTION_SET_SERVER_CONFIG)) { 151 logd("ACTION_SET_SERVER_CONFIG received"); 152 String hostUrl = intent.getStringExtra(DMIntent.FIELD_SERVER_URL); 153 String proxyAddress = intent.getStringExtra(DMIntent.FIELD_PROXY_ADDRESS); 154 logd("server URL: " + hostUrl + " proxy address: " + proxyAddress); 155 DMHelper.setServerUrl(context, hostUrl); 156 DMHelper.setProxyHostname(context, proxyAddress); 157 } else if (action.equals(DMIntent.ACTION_CANCEL_SESSION)) { 158 // create intent and start DM service 159 Intent newIntent = new Intent(DMIntent.LAUNCH_INTENT); 160 newIntent.putExtra(DMIntent.FIELD_TYPE, DMIntent.TYPE_CANCEL_DM_SESSION); 161 logd("cancelling DM Session"); 162 newIntent.setClass(context, DMClientService.class); 163 context.startService(newIntent); 164 } 165 } 166 167 // handle client-initiated FOTA intents 168 private void handleClientInitiatedFotaIntent(Context context, Intent intent) { 169 String strServerID = intent.getStringExtra(DMIntent.FIELD_SERVERID); 170 if (TextUtils.isEmpty(strServerID)) { 171 logd("Error! Can't start FOTA session: " + 172 DMIntent.FIELD_SERVERID + " is null or an empty string."); 173 return; 174 } 175 176 String alertString = intent.getStringExtra(DMIntent.FIELD_ALERT_STR); 177 if (TextUtils.isEmpty(alertString)) { 178 logd("Error! Can't start FOTA session: " + 179 DMIntent.FIELD_ALERT_STR + " is null or an empty string."); 180 return; 181 } 182 183 setState(context, DMHelper.STATE_APPROVED_BY_USER); 184 long requestID = System.nanoTime(); 185 186 // Save pending info here 187 SharedPreferences p = context.getSharedPreferences(DMHelper.DM_PREFERENCES_KEY, 0); 188 SharedPreferences.Editor ed = p.edit(); 189 ed.putInt(DMHelper.DM_SESSION_TYPE_KEY, DMIntent.TYPE_FOTA_CLIENT_SESSION_REQUEST); 190 ed.putLong(DMHelper.MESSAGE_TIMESTAMP_ID_KEY, requestID); 191 ed.putString(DMHelper.FOTA_SERVER_ID_KEY, strServerID); 192 ed.putString(DMHelper.FOTA_ALERT_STRING_KEY, alertString); 193 ed.apply(); 194 195 if (isWifiConnected(context) || isDataNetworkAcceptable(context)) { 196 if (!isWifiConnected(context) && isDataNetworkAcceptable(context) && isPhoneTypeLTE()) { 197 logd("handleClientInitiatedFotaIntent, start apn monitoring service" 198 + " for requestID " + requestID); 199 setFotaApnState(context, DMHelper.FOTA_APN_STATE_START_DM_SESSION); 200 startDataConnectionService(context); 201 } else { 202 logd("handleClientInitiatedFotaIntent starting DM session"); 203 startDMSession(context); 204 } 205 } else { 206 logd("handleClientInitiatedFotaIntent: start data/call state monitoring"); 207 startDataConnectionService(context); 208 } 209 } 210 211 // handle SMS and WAP Push intents; 212 private void handleWapPushIntent(Context context, Intent intent) { 213 214 int currentState = getState(context); 215 216 logd("handleWapPushIntent() current state: " + currentState); 217 218 // if current state is already "session in progress" - ignore new message; 219 // otherwise remove old message and process a new one. 220 if (currentState == DMHelper.STATE_SESSION_IN_PROGRESS) { 221 loge("current state is 'Session-in-Progress', ignoring new message."); 222 return; 223 } 224 225 DMHelper.cleanAllResources(context); 226 227 //clean fota apn resources and stop using fota apn 228 if (isPhoneTypeLTE()) { 229 int mgetFotaApnState = getFotaApnState(context); 230 logd("handleWapPushIntent, check if necessary to stop using fota apn " 231 + mgetFotaApnState); 232 if (mgetFotaApnState != DMHelper.FOTA_APN_STATE_INIT) { 233 // resetting FOTA APN STATE 234 logd("XXX resetting FOTA APN state"); 235 setFotaApnState(context, DMHelper.FOTA_APN_STATE_INIT); 236 stopUsingFotaApn(context); 237 DMHelper.cleanFotaApnResources(context); 238 } 239 } 240 241 //parse & save message; get UI mode 242 boolean result = parseAndSaveWapPushMessage(context, intent); 243 244 if (!result) { 245 loge("handleWapPushIntent(): error in parseAndSaveWapPushMessage()"); 246 DMHelper.cleanAllResources(context); 247 return; 248 } 249 250 if (treeExist(context) || !isPhoneTypeLTE()) { 251 //check UI mode and prepare and start process 252 preprocess(context, currentState); 253 } else { 254 logd("WapPush arrived before tree initialization"); 255 initialWapPending = true; 256 Intent intentConnmoInit = new Intent("com.android.omadm.service.wait_timer_alert"); 257 context.sendBroadcast(intentConnmoInit); 258 } 259 } 260 261 private void handleUserConfirmedSession(Context context) { 262 // check if message is not expired 263 if (DMHelper.isMessageExpired(context)) { 264 DMHelper.cleanAllResources(context); 265 logd("handleUserConfirmedSession(): message is expired."); 266 return; 267 } 268 269 startProcess(context); 270 } 271 272 // handle boot completed intent 273 private void handleBootCompletedIntent(Context context) { 274 // Check if DM tree already has been generated. Start service to generate tree 275 // in case if required. It may happened only once during first boot. 276 if (!treeExist(context)) { 277 logd("Boot completed: there is no DM Tree. Start service to generate tree."); 278 Intent intent = new Intent(DMIntent.LAUNCH_INTENT); 279 intent.putExtra("NodePath", "."); 280 intent.putExtra(DMIntent.FIELD_REQUEST_ID, -2L); 281 intent.setClass(context, DMClientService.class); 282 context.startService(intent); 283 if (!initialWapPending) { 284 logd("handleBootCompletedIntent, no initial WapPush pending."); 285 DMHelper.cleanAllResources(context); 286 return; 287 } else { 288 logd("handleBootCompletedIntent, initial WapPush pending."); 289 initialWapPending = false; 290 setState(context, DMHelper.STATE_PENDING_MESSAGE); 291 } 292 } 293 294 // if (isPhoneTypeLTE()) { 295 // int fotaApnState = getFotaApnState(context); 296 // logd("handleBootCompletedIntent, check if need to stop using fota apn " 297 // + fotaApnState); 298 // if (fotaApnState != DMHelper.FOTA_APN_STATE_INIT) { 299 // resetting FOTA APN STATE 300 // setFotaApnState(context, DMHelper.FOTA_APN_STATE_INIT); 301 //stopUsingFotaApn(); 302 // DMHelper.cleanFotaApnResources(context); 303 // } 304 // stopUsingFotaApn(); 305 // } 306 307 int currentState = getState(context); 308 309 if (currentState == DMHelper.STATE_IDLE) { 310 logd("Boot completed: there is no message to proceed."); 311 return; 312 } 313 314 // check if message is not expired 315 if (DMHelper.isMessageExpired(context)) { 316 DMHelper.cleanAllResources(context); 317 logd("handleBootCompletedIntent(): the message is expired."); 318 return; 319 } 320 321 // initiate mUIMode and mData from preferences 322 if (!initFromSharedPreferences(context)) { 323 DMHelper.cleanAllResources(context); 324 logd("handleBootCompletedIntent(): cannot init from shared preferences"); 325 return; 326 } 327 328 preprocess(context, currentState); 329 } 330 331 // handle time alert intent (all instances) 332 private void handleTimeAlertIntent(Context context) { 333 int currentState = getState(context); 334 switch (currentState) { 335 case DMHelper.STATE_IDLE: 336 // nothing there 337 DMHelper.cleanAllResources(context); 338 logd("Time alert: there is no message to proceed."); 339 break; 340 } 341 342 if (currentState == DMHelper.STATE_IDLE) { 343 // nothing there 344 DMHelper.cleanAllResources(context); 345 logd("Time alert: there is no message to proceed."); 346 } else if (DMHelper.isMessageExpired(context)) { 347 // check if message is not expired 348 DMHelper.cleanAllResources(context); 349 logd("Warning from handleTimeAlertIntent(): the message is expired."); 350 } else if (currentState == DMHelper.STATE_SESSION_IN_PROGRESS) { 351 // session in progress; doing nothing. 352 logd("Time alert: session in progress; doing nothing."); 353 DMHelper.subscribeForTimeAlert(context, 354 DMHelper.TIME_CHECK_STATUS_AFTER_STARTING_DM_SERVICE); 355 } else if (currentState == DMHelper.STATE_APPROVED_BY_USER) { 356 // approved by user: try to start session or data/call monitoring service 357 logd("Time alert: state 'approved by user'; starting process"); 358 startProcess(context); 359 } else if (currentState == DMHelper.STATE_PENDING_MESSAGE) { 360 // approved by user: try to start session or data/call monitoring service pending 361 logd("Time alert: state 'pending message'; read from preferences starting preprocess"); 362 363 // initiate mUIMode and mData from preferences 364 if (!initFromSharedPreferences(context)) { 365 DMHelper.cleanAllResources(context); 366 logd("Warning from handleTimeAlertIntent(): cannot init from shared preferences"); 367 return; 368 } 369 preprocess(context, currentState); 370 } else { 371 loge("Error from handleTimeAlertIntent(): unknown state " + currentState); 372 } 373 } 374 375 private static void handleNotifyResultToServer(Context context, Intent intent) { 376 logd("Inside handleNotifyResultToServer"); 377 378 // Save message 379 SharedPreferences p = context.getSharedPreferences(DMHelper.FOTA_APN_PREFERENCE_KEY, 0); 380 SharedPreferences.Editor ed = p.edit(); 381 382 ed.putString(DMHelper.LAWMO_RESULT_KEY, intent.getStringExtra(DMIntent.FIELD_LAWMO_RESULT)); 383 ed.putString(DMHelper.FOTA_RESULT_KEY, intent.getStringExtra(DMIntent.FIELD_FOTA_RESULT)); 384 ed.putString(DMHelper.PKG_URI_KEY, intent.getStringExtra(DMIntent.FIELD_PKGURI)); 385 ed.putString(DMHelper.ALERT_TYPE_KEY, intent.getStringExtra(DMIntent.FIELD_ALERTTYPE)); 386 ed.putString(DMHelper.CORRELATOR_KEY, intent.getStringExtra(DMIntent.FIELD_CORR)); 387 ed.putString(DMHelper.SERVER_ID_KEY, intent.getStringExtra(DMIntent.FIELD_SERVERID)); 388 389 ed.apply(); 390 391 if (isDataNetworkAcceptable(context) && !isWifiConnected(context) && isPhoneTypeLTE()) { 392 int mgetFotaApnState = getFotaApnState(context); 393 if (mgetFotaApnState != DMHelper.FOTA_APN_STATE_INIT) { 394 logd("there must be a pending session, return"); 395 return; 396 } 397 // for LTE and eHRPD coverage , switch the apn before FDM 398 logd("handleNotifyResultToServer starting FOTA APN"); 399 setFotaApnState(context, DMHelper.FOTA_APN_STATE_REPORT_DM_SESSION); 400 startDataConnectionService(context); 401 } else { 402 sendNotifyIntent(context); 403 } 404 } 405 406 // start session if we have network connectivity 407 private void handleDataConnectionReady(Context context) { 408 logd("Inside handleDataConnectionReady"); 409 int fotaApnState = getFotaApnState(context); 410 logd("FOTA APN state is " + fotaApnState); 411 412 if (fotaApnState == DMHelper.FOTA_APN_STATE_REPORT_DM_SESSION) { 413 setFotaApnState(context, DMHelper.FOTA_APN_STATE_REPORT_DM_SESSION_RPTD); 414 sendNotifyIntent(context); 415 } else if (fotaApnState == DMHelper.FOTA_APN_STATE_START_DM_SESSION) { 416 setFotaApnState(context, DMHelper.FOTA_APN_STATE_START_DM_SESSION_RPTD); 417 // check if message is not expired 418 if (DMHelper.isMessageExpired(context)) { 419 DMHelper.cleanAllResources(context); 420 logd("Warning from handleApnStateActive(): the message is expired."); 421 return; 422 } 423 424 int currentState = getState(context); 425 426 // nothing to do here 427 if (currentState == DMHelper.STATE_IDLE) { 428 DMHelper.cleanAllResources(context); 429 logd("handleApnStateActive(): there is no message to proceed."); 430 return; 431 } 432 433 if (currentState == DMHelper.STATE_SESSION_IN_PROGRESS) { 434 logd("handleApnStateActive(): session in progress; doing nothing."); 435 return; 436 } 437 438 startDMSession(context); 439 } else { 440 logd("handleApnStateActive: NO ACTION NEEDED"); 441 } 442 } 443 444 // check UI mode and prepare and start process 445 private void preprocess(Context context, int currentState) { 446 447 setState(context, currentState); 448 449 logd("From preprocess().... Current state = " + currentState); 450 451 // check UI mode. If updates has been replaced with the new one and user already 452 // confirmed - we are skipping confirmation. 453 if (mUIMode == DMHelper.UI_MODE_CONFIRMATION 454 && currentState != DMHelper.STATE_APPROVED_BY_USER) { 455 456 // user confirmation is required 457 logd("User confirmation is required"); 458 DMHelper.postConfirmationNotification(context); 459 setState(context, DMHelper.STATE_PENDING_MESSAGE); 460 461 // check and repost notification in case user cancels it 462 DMHelper.subscribeForTimeAlert(context, 463 DMHelper.TIME_CHECK_NOTIFICATION_AFTER_SUBSCRIPTION); 464 465 return; 466 } 467 468 if (mUIMode == DMHelper.UI_MODE_INFORMATIVE) { 469 // required notification, just inform the user 470 logd("User notification is required"); 471 DMHelper.postInformativeNotification_message1(context); 472 } else { 473 logd("Silent DM session: silent mode or user already has approved."); 474 } 475 476 // try to start DM session or start Data and Call State Monitoring Service 477 startProcess(context); 478 } 479 480 // parse data from intent; set UI mode; save required data. 481 private boolean parseAndSaveWapPushMessage(Context context, Intent intent) { 482 483 // Parse message 484 Bundle bdl = intent.getExtras(); 485 byte[] data = bdl.getByteArray("data"); 486 mData = data; 487 488 if (data == null || data.length < 25) { 489 loge("parseAndSaveWapPushMessage: data[] is null or length < 25."); 490 return false; 491 } 492 493 // first 16 bytes - digest 494 int version = ((data[17] >> 6) & 0x3) | ((data[16]) << 2); 495 int uiMode = (data[17] >> 4) & 0x3; 496 int indicator = (data[17] >> 3) & 0x1; 497 int sessionId = ((data[21] & 0xff) << 8) | data[22]; 498 int serverIdLength = data[23]; // must be equal to data.length-24 499 500 if (serverIdLength <= 0) { 501 loge("parseAndSaveWapPushMessage: serverIdLength is invalid: " + serverIdLength); 502 return false; 503 } 504 505 String serverId = new String(data, 24, serverIdLength, StandardCharsets.UTF_8); 506 mUIMode = uiMode; 507 508 // fixme: treating invalid uimode as informative for now for Sprint 509 if(mUIMode != DMHelper.UI_MODE_CONFIRMATION && mUIMode != DMHelper.UI_MODE_INFORMATIVE) { 510 TelephonyManager tm = TelephonyManager.from(context); 511 String simOperator = tm.getSimOperator(); 512 String imsi = tm.getSubscriberId(); 513 Log.d(TAG, "simOperator: " + simOperator + " IMSI: " + imsi); 514 if ("310120".equals(simOperator) || (imsi != null && imsi.startsWith("310120"))) { 515 loge("parseAndSaveWapPushMessage: UICC is sprint. Received uimode " + uiMode + 516 "; changing to informative"); 517 mUIMode = DMHelper.UI_MODE_INFORMATIVE; 518 } 519 } 520 521 if (DBG) { 522 Log.i(TAG, "Get Provision Package0" 523 + " version:" + version 524 + " uiMode:" + uiMode 525 + " indicator:" + indicator 526 + " sessionId:" + sessionId 527 + " serverId:" + serverId); 528 } 529 530 // Save message 531 SharedPreferences p = context.getSharedPreferences(DMHelper.DM_PREFERENCES_KEY, 0); 532 SharedPreferences.Editor ed = p.edit(); 533 //ed.putInt("type", DMIntent.TYPE_PKG0_NOTIFICATION); 534 //ed.putLong(DMHelper.REQUEST_ID_KEY, System.currentTimeMillis()); 535 ed.putInt("length", data.length); 536 ed.putInt(DMHelper.DM_SESSION_TYPE_KEY, DMIntent.TYPE_PKG0_NOTIFICATION); 537 ed.putLong(DMHelper.MESSAGE_TIMESTAMP_ID_KEY, System.nanoTime()); 538 ed.putInt(DMHelper.DM_UI_MODE_KEY, mUIMode); 539 540 ed.apply(); 541 542 // PendingResult pendingResult = goAsync(); 543 // DMParseSaveWapMsgRunnable dmParseSaveWapMsgRunnable 544 // = new DMParseSaveWapMsgRunnable(pendingResult); 545 // Thread dmParseSaveWapMsgThread = new Thread(dmParseSaveWapMsgRunnable); 546 // dmParseSaveWapMsgThread.start(); 547 548 // TODO: move to worker thread 549 try { 550 FileOutputStream out = new FileOutputStream(DMHelper.POSTPONED_DATA_PATH); 551 out.write(mData); 552 out.close(); 553 } catch (IOException e) { 554 loge("IOException while creating dmpostponed.dat", e); 555 } 556 557 return true; 558 } 559 560 // TODO: remove or uncomment to use for saving file asynchronously 561 // class DMParseSaveWapMsgRunnable implements Runnable { 562 // /** Pending result to call finish() when thread returns. */ 563 // private final PendingResult mPendingResult; 564 // 565 // DMParseSaveWapMsgRunnable(PendingResult pendingResult) { 566 // mPendingResult = pendingResult; 567 // } 568 // 569 // @Override 570 // public void run() { 571 // logd("Enter dmParseSaveWapMsgThread tid=" + Thread.currentThread().getId()); 572 // try { 573 // FileOutputStream out = new FileOutputStream(DMHelper.POSTPONED_DATA_PATH); 574 // out.write(mData); 575 // out.close(); 576 // } catch (IOException e) { 577 // loge("IOException while creating dmpostponed.dat", e); 578 // } finally { 579 // mPendingResult.finish(); 580 // } 581 // } 582 // } 583 584 //try to start DM session or starts Data and Call State Monitoring Service 585 private void startProcess(Context context) { 586 //wrj348 - VZW customization: reject the wap push if phone is in ECB mode or Roaming 587 if (!allowDMSession()) { 588 return; 589 } 590 setState(context, DMHelper.STATE_APPROVED_BY_USER); 591 592 // Start DM session if wifi is available. 593 if (isWifiConnected(context)) { 594 startDMSession(context); 595 } else { 596 // request FOTA APN 597 logd("startProcess(), start data connection service"); 598 setFotaApnState(context, DMHelper.FOTA_APN_STATE_START_DM_SESSION); 599 startDataConnectionService(context); 600 } 601 } 602 603 private static boolean allowDMSession() { 604 if (isInECBMode()) { 605 return false; 606 } 607 608 Log.i(TAG, "DMSession allowed - don't reject"); 609 return true; 610 } 611 612 /** 613 * Returns whether phone is in emergency callback mode. 614 * @return true if the phone is in ECB mode; false if not 615 */ 616 private static boolean isInECBMode() { 617 boolean ecbMode = SystemProperties.getBoolean( 618 TelephonyProperties.PROPERTY_INECM_MODE, false); 619 Log.i(TAG, "Phone ECB status: " + ecbMode); 620 return ecbMode; 621 } 622 623 //start DM session. 624 private void startDMSession(Context context) { 625 logd("startDMSession"); 626 // get request ID from the shared preferences (the message time stamp used) 627 SharedPreferences p = context.getSharedPreferences(DMHelper.DM_PREFERENCES_KEY, 0); 628 629 long requestID = p.getLong(DMHelper.MESSAGE_TIMESTAMP_ID_KEY, -1); 630 int type = p.getInt(DMHelper.DM_SESSION_TYPE_KEY, -1); 631 632 // create intent and start DM service 633 Intent intent = new Intent(DMIntent.LAUNCH_INTENT); 634 intent.putExtra(DMIntent.FIELD_REQUEST_ID, requestID); 635 intent.putExtra(DMIntent.FIELD_TYPE, type); 636 637 if (type == DMIntent.TYPE_FOTA_CLIENT_SESSION_REQUEST) { 638 String serverID = p.getString(DMHelper.FOTA_SERVER_ID_KEY, null); 639 String alertString = p.getString(DMHelper.FOTA_ALERT_STRING_KEY, null); 640 intent.putExtra(DMIntent.FIELD_TYPE, DMIntent.TYPE_FOTA_CLIENT_SESSION_REQUEST); 641 intent.putExtra(DMIntent.FIELD_SERVERID, serverID); 642 intent.putExtra(DMIntent.FIELD_ALERT_STR, alertString); 643 logd("starting TYPE_FOTA_CLIENT_SESSION_REQUEST: serverID=" 644 + serverID + " alertString=" + alertString 645 + " requestID=" + requestID); 646 } else { 647 // package 0 notification 648 intent.putExtra(DMIntent.FIELD_TYPE, type); 649 650 if (mData == null) { // session has not been started right away after receiving a message. 651 mData = setDataFromFile(context); 652 653 if (mData == null) { 654 logd("Error. Cannot read data from file dmpostponed.dat"); 655 DMHelper.cleanAllResources(context); 656 return; 657 } 658 } 659 660 intent.putExtra(DMIntent.FIELD_PKG0, mData); 661 } 662 663 increaseDMSessionAttempt(context); 664 665 setState(context, DMHelper.STATE_SESSION_IN_PROGRESS); 666 667 DMHelper.subscribeForTimeAlert(context, 668 DMHelper.TIME_CHECK_STATUS_AFTER_STARTING_DM_SERVICE); 669 670 intent.setClass(context, DMClientService.class); 671 context.startService(intent); 672 } 673 674 // start data connection service 675 private static void startDataConnectionService(Context context) { 676 logd("Inside startDataConnectionService"); 677 DMHelper.subscribeForTimeAlert(context, 678 DMHelper.TIME_CHECK_STATUS_AFTER_STARTING_MONITORING_SERVICE); 679 Intent intent = new Intent(DMIntent.ACTION_START_DATA_CONNECTION_SERVICE); 680 intent.setClass(context, DMDataConnectionService.class); 681 context.startService(intent); 682 } 683 684 // stop data connection service 685 private static void stopDataConnectionService(Context context) { 686 logd("Inside stopDataConnectionService"); 687 Intent intent = new Intent(DMIntent.ACTION_START_DATA_CONNECTION_SERVICE); 688 intent.setClass(context, DMDataConnectionService.class); 689 context.stopService(intent); 690 } 691 692 // Verify session result: if result is successful, clean all resources. 693 // Otherwise, try to resubmit session request. 694 private void handleDmServiceResult(Context context, Intent intent) { 695 // check if request ID from incoming intent match to the one which has been sent and saved 696 // get request ID from the shared preferences (the message time stamp used) 697 SharedPreferences p = context.getSharedPreferences(DMHelper.DM_PREFERENCES_KEY, 0); 698 SharedPreferences.Editor ed = p.edit(); 699 long savedRequestId = p.getLong(DMHelper.MESSAGE_TIMESTAMP_ID_KEY, 0); 700 long receivedRequestId = intent.getLongExtra(DMIntent.FIELD_REQUEST_ID, -1); 701 702 if (receivedRequestId == -2) { 703 logd("handleDmServiceResult, tree initialisation session."); 704 return; 705 } 706 707 // clear fota apn resources and stop using fota apn 708 if (isPhoneTypeLTE()) { 709 int fotaApnState = getFotaApnState(context); 710 logd("handleDmServiceResult, chk if need to stop using fota apn " 711 + fotaApnState); 712 if (fotaApnState != DMHelper.FOTA_APN_STATE_INIT) { 713 // resetting FOTA APN STATE 714 setFotaApnState(context, DMHelper.FOTA_APN_STATE_INIT); 715 stopUsingFotaApn(context); 716 // removing shared prefs settings 717 DMHelper.cleanFotaApnResources(context); 718 } 719 stopUsingFotaApn(context); 720 } 721 722 if (savedRequestId != receivedRequestId) { 723 loge("request ID " + receivedRequestId + " from result intent doesn't " 724 + "match saved request ID " + savedRequestId + ", ignored"); 725 // return; 726 } 727 728 int sessionResult = intent.getIntExtra(DMIntent.FIELD_DMRESULT, -1); 729 730 int uiMode = p.getInt(DMHelper.DM_UI_MODE_KEY, -1); 731 mUIMode = uiMode; 732 logd("mUIMode is: " + uiMode); 733 if (uiMode == DMHelper.UI_MODE_INFORMATIVE) { 734 if (sessionResult == DMResult.SYNCML_DM_SUCCESS) { 735 logd("Displaying success notification message2"); 736 DMHelper.postInformativeNotification_message2_success(context); 737 } else { 738 logd("Displaying Fail notification message2"); 739 DMHelper.postInformativeNotification_message2_fail(context); 740 } 741 ed.putInt(DMHelper.DM_UI_MODE_KEY, -1); 742 ed.apply(); 743 } 744 745 if (sessionResult == DMResult.SYNCML_DM_SUCCESS) { 746 DMHelper.cleanAllResources(context); 747 logd("Finished success."); 748 return; 749 } 750 751 if (!canRestartSession(context, p)) { 752 DMHelper.cleanAllResources(context); 753 return; 754 } 755 756 // update status in the preferences 757 setState(context, DMHelper.STATE_APPROVED_BY_USER); 758 759 //subscribe for the time alert to start DM session again after TIME_BETWEEN_SESSION_ATTEMPTS. 760 DMHelper.subscribeForTimeAlert(context, DMHelper.TIME_BETWEEN_SESSION_ATTEMPTS); 761 } 762 763 // check if session request can be resubmitted (if message still valid and 764 // number of tries doesn't exceed MAX) 765 private static boolean canRestartSession(Context context, SharedPreferences p) { 766 int numberOfSessionAttempts = p.getInt(DMHelper.DM_SESSION_ATTEMPTS_KEY, -1); 767 768 // check if max number has not been exceeded 769 if (numberOfSessionAttempts > DMHelper.MAX_SESSION_ATTEMPTS) { 770 logd("Error. Number of attempts to start DM session exceed MAX."); 771 return false; 772 } 773 774 // check if message is expired or not 775 if (DMHelper.isMessageExpired(context)) { 776 logd("Error from canRestartSession(): the message is expired."); 777 return false; 778 } 779 780 return true; 781 } 782 783 // set current state 784 private static void setState(Context context, int state) { 785 SharedPreferences p = context.getSharedPreferences(DMHelper.DM_PREFERENCES_KEY, 0); 786 SharedPreferences.Editor ed = p.edit(); 787 ed.putInt(DMHelper.STATE_KEY, state); 788 ed.apply(); 789 } 790 791 /** 792 * Get current state from shared prefs. If state is "Session In Progress", verify that the DM 793 * session didn't fail and also has the same status, otherwise current state will be changed 794 * to "Approved by User" and will be ready to handle a request for a new DM session. 795 * 796 * @param context the context to use 797 * @return the current state 798 */ 799 private static int getState(Context context) { 800 SharedPreferences p = context.getSharedPreferences(DMHelper.DM_PREFERENCES_KEY, 0); 801 int currentState = p.getInt(DMHelper.STATE_KEY, 0); 802 803 if (currentState == DMHelper.STATE_SESSION_IN_PROGRESS 804 && !DMClientService.sIsDMSessionInProgress) { 805 currentState = DMHelper.STATE_APPROVED_BY_USER; 806 setState(context, currentState); 807 } 808 return currentState; 809 } 810 811 // increase attempt to start DM session 812 private static void increaseDMSessionAttempt(Context context) { 813 SharedPreferences p = context.getSharedPreferences(DMHelper.DM_PREFERENCES_KEY, 0); 814 int numberOfSessionAttempts = p.getInt(DMHelper.DM_SESSION_ATTEMPTS_KEY, 0); 815 SharedPreferences.Editor ed = p.edit(); 816 ed.putInt(DMHelper.DM_SESSION_ATTEMPTS_KEY, (numberOfSessionAttempts + 1)); 817 ed.apply(); 818 } 819 820 // check and initialize variables from preferences 821 private boolean initFromSharedPreferences(Context context) { 822 SharedPreferences p = context.getSharedPreferences(DMHelper.DM_PREFERENCES_KEY, 0); 823 long timestamp = p.getLong(DMHelper.MESSAGE_TIMESTAMP_ID_KEY, -1); 824 mUIMode = p.getInt(DMHelper.DM_UI_MODE_KEY, -1); 825 mData = setDataFromFile(context); 826 boolean success = !(timestamp <= 0 || mUIMode < 0); 827 if (DBG) logd("initFromSharedPreferences: " + (success ? "ok" : "fail")); 828 return success; 829 } 830 831 private static byte[] setDataFromFile(Context context) { 832 SharedPreferences p = context.getSharedPreferences(DMHelper.DM_PREFERENCES_KEY, 0); 833 int length = p.getInt("length", -1); 834 835 if (length <= 0) { 836 //logd("Error. Invalid postponed data length."); 837 return null; 838 } 839 840 byte[] data = new byte[length]; 841 842 try { 843 FileInputStream in = new FileInputStream(DMHelper.POSTPONED_DATA_PATH); 844 if (in.read(data) <= 0) { 845 logd("Invalid postponed data."); 846 in.close(); 847 return null; 848 } 849 in.close(); 850 return data; 851 } catch (IOException e) { 852 loge("IOException", e); 853 return null; 854 } 855 } 856 857 private static boolean treeExist(Context context) { 858 if (context != null) { 859 String strTreeHomeDir = context.getFilesDir().getAbsolutePath() + "/dm"; 860 File dirDes = new File(strTreeHomeDir); 861 862 if (dirDes.exists() && dirDes.isDirectory()) { 863 logd("DM Tree exists:" + strTreeHomeDir); 864 return true; 865 } else { 866 logd("DM Tree NOT exists:" + strTreeHomeDir); 867 return false; 868 } 869 } else { 870 return false; 871 } 872 } 873 874 // set current state 875 private static void setFotaApnState(Context context, int state) { 876 logd("setFotaApnState: " + state); 877 SharedPreferences p = context.getSharedPreferences(DMHelper.FOTA_APN_PREFERENCE_KEY, 0); 878 SharedPreferences.Editor ed = p.edit(); 879 ed.putInt(DMHelper.FOTA_APN_STATE_KEY, state); 880 ed.apply(); 881 } 882 883 // get current state. 884 private static int getFotaApnState(Context context) { 885 SharedPreferences p = context.getSharedPreferences(DMHelper.FOTA_APN_PREFERENCE_KEY, 0); 886 return p.getInt(DMHelper.FOTA_APN_STATE_KEY, 0); 887 } 888 889 /** 890 * Stop using the FOTA APN. 891 * @param context the BroadcastReceiver context 892 */ 893 private static void stopUsingFotaApn(Context context) { 894 logd("stopUsingFotaApn"); 895 896 ConnectivityManager connMgr = (ConnectivityManager) context 897 .getSystemService(Context.CONNECTIVITY_SERVICE); 898 int result = connMgr.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, 899 Phone.FEATURE_ENABLE_FOTA); 900 if (result != -1) { 901 Log.w(TAG, "stopUsingNetworkFeature result=" + result); 902 } 903 stopDataConnectionService(context); 904 } 905 906 // Function which will send intents to start FDM 907 private static void sendNotifyIntent(Context context) { 908 logd("Inside sendNotifyIntent"); 909 910 SharedPreferences p = context.getSharedPreferences(DMHelper.FOTA_APN_PREFERENCE_KEY, 0); 911 String lawmoResult = p.getString(DMHelper.LAWMO_RESULT_KEY, null); 912 String fotaResult = p.getString(DMHelper.FOTA_RESULT_KEY, null); 913 String pkgURI = p.getString(DMHelper.PKG_URI_KEY, null); 914 String alertType = p.getString(DMHelper.ALERT_TYPE_KEY, null); 915 String correlator = p.getString(DMHelper.CORRELATOR_KEY, null); 916 String serverID = p.getString(DMHelper.SERVER_ID_KEY, null); 917 918 logd("sendNotifyIntent Input==>\n" + " lawmoResult=" 919 + lawmoResult + '\n' + "fotaResult=" 920 + fotaResult + '\n' + " pkgURI=" 921 + pkgURI + '\n' + " alertType=" 922 + alertType + '\n' + " serverID=" 923 + serverID + '\n' + " correlator=" 924 + correlator); 925 926 if (alertType.equals(ALERT_TYPE_DOWNLOADANDUPDATE)) { 927 // Need to send an intent for doing a FOTA FDM session 928 Intent fotafdmintent = new Intent(DMIntent.LAUNCH_INTENT); 929 fotafdmintent.putExtra(DMIntent.FIELD_TYPE, DMIntent.TYPE_FOTA_NOTIFY_SERVER); 930 fotafdmintent.putExtra(DMIntent.FIELD_FOTA_RESULT, fotaResult); 931 fotafdmintent.putExtra(DMIntent.FIELD_PKGURI, pkgURI); 932 fotafdmintent.putExtra(DMIntent.FIELD_ALERTTYPE, alertType); 933 fotafdmintent.putExtra(DMIntent.FIELD_SERVERID, serverID); 934 fotafdmintent.putExtra(DMIntent.FIELD_CORR, correlator); 935 fotafdmintent.setClass(context, DMClientService.class); 936 context.startService(fotafdmintent); 937 } else if (pkgURI.equals(RP_OPERATIONS_FACTORYRESET) || pkgURI 938 .equals(RP_EXT_OPERATIONS_RESET)) { 939 // LAWMO FDM session 940 Intent lawmofdmintent = new Intent(DMIntent.LAUNCH_INTENT); 941 lawmofdmintent.putExtra(DMIntent.FIELD_TYPE, DMIntent.TYPE_LAWMO_NOTIFY_SESSION); 942 lawmofdmintent.putExtra(DMIntent.FIELD_LAWMO_RESULT, lawmoResult); 943 lawmofdmintent.putExtra(DMIntent.FIELD_PKGURI, pkgURI); 944 lawmofdmintent.putExtra(DMIntent.FIELD_ALERTTYPE, ""); 945 lawmofdmintent.putExtra(DMIntent.FIELD_CORR, ""); 946 lawmofdmintent.setClass(context, DMClientService.class); 947 context.startService(lawmofdmintent); 948 } else { 949 // just return for now 950 logd("No Action, Just return for now"); 951 } 952 } 953 954 private static boolean isWifiConnected(Context context) { 955 logd("Inside isWifiConnected"); 956 957 ConnectivityManager cm = (ConnectivityManager) context 958 .getSystemService(Context.CONNECTIVITY_SERVICE); 959 if (cm == null) { 960 logd("can't get Connectivity Service"); 961 return false; 962 } 963 964 NetworkInfo ni = cm.getActiveNetworkInfo(); 965 if (ni == null) { 966 logd("NetworkInfo is null"); 967 return false; 968 } 969 if (!ni.isConnected()) { 970 logd("Network is not connected"); 971 return false; 972 } 973 if (ni.getType() != ConnectivityManager.TYPE_WIFI) { 974 logd("network type is not wifi"); 975 return false; 976 } 977 978 // return true only when WiFi is connected 979 return true; 980 } 981 982 983 private static boolean isPhoneTypeLTE() { 984 return DMSettingsHelper.isPhoneTypeLTE(); 985 } 986 987 private static boolean isPhoneTypeCDMA3G(Context context) { 988 TelephonyManager tm = (TelephonyManager) context 989 .getSystemService(Context.TELEPHONY_SERVICE); 990 if ((tm.getCurrentPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) && !isPhoneTypeLTE()) { 991 logd("3G CDMA phone"); 992 return true; 993 } 994 logd("Non-CDMA or 4G Device"); 995 return false; 996 } 997 998 // check if we can set up a mobile data connection on this network type 999 private static boolean isDataNetworkAcceptable(Context context) { 1000 TelephonyManager tm = (TelephonyManager) context 1001 .getSystemService(Context.TELEPHONY_SERVICE); 1002 1003 int callState = tm.getCallState(); 1004 if (callState != TelephonyManager.CALL_STATE_IDLE) { 1005 logd("Call state not idle: " + callState); 1006 return false; 1007 } 1008 1009 int dataNetworkType = tm.getDataNetworkType(); 1010 switch (dataNetworkType) { 1011 case TelephonyManager.NETWORK_TYPE_EVDO_0: 1012 case TelephonyManager.NETWORK_TYPE_EVDO_A: 1013 case TelephonyManager.NETWORK_TYPE_1xRTT: 1014 case TelephonyManager.NETWORK_TYPE_EVDO_B: 1015 case TelephonyManager.NETWORK_TYPE_LTE: 1016 case TelephonyManager.NETWORK_TYPE_EHRPD: 1017 logd("Data network type is acceptable: " + dataNetworkType); 1018 return true; 1019 1020 default: 1021 logd("Data network type is not acceptable: " + dataNetworkType); 1022 return false; 1023 } 1024 } 1025 1026 private static void saveDevDetail(Context context) { 1027 logd("Inside saveDevDetail"); 1028 1029 String swVer = SystemProperties.get("ro.build.version.full"); 1030 if (TextUtils.isEmpty(swVer)) { 1031 swVer = "Unknown"; 1032 } 1033 1034 SharedPreferences p = context.getSharedPreferences(DEV_DETAIL, 0); 1035 String currFwV = p.getString(CURR_FW_VER, null); 1036 //String preFwV = p.getString(PRE_FW_VER, null); 1037 1038 SharedPreferences.Editor ed = p.edit(); 1039 if (TextUtils.isEmpty(currFwV)) { 1040 logd("First powerup or powerup after FDR, save current SwV"); 1041 ed.putString(CURR_FW_VER, swVer); 1042 } else if (!(currFwV.equals(swVer))) { 1043 logd("System Update success, save previous FwV and LastUpdateTime"); 1044 ed.putString(PRE_FW_VER, currFwV); 1045 ed.putString(CURR_FW_VER, swVer); 1046 SimpleDateFormat simpleDateFormat; 1047 if (isPhoneTypeLTE()) { 1048 simpleDateFormat = new SimpleDateFormat("MM:dd:yyyy:HH:mm", Locale.US); 1049 simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 1050 } else { 1051 simpleDateFormat = new SimpleDateFormat("MM:dd:yy:HH:mm:ss:z", Locale.US); 1052 } 1053 String currTime = simpleDateFormat.format(new Date(System.currentTimeMillis())); 1054 ed.putString(LAST_UPD_TIME, currTime); 1055 } 1056 WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 1057 WifiInfo wi = wm.getConnectionInfo(); 1058 String wMacAddr = (wi == null) ? null : wi.getMacAddress(); 1059 logd("WiFi Mac address " + wMacAddr); 1060 if (!TextUtils.isEmpty(wMacAddr)) { 1061 ed.putString(WIFI_MAC_ADDR, wMacAddr); 1062 } 1063 ed.apply(); 1064 } 1065 1066 private static void logd(String msg) { 1067 Log.d(TAG, msg); 1068 } 1069 1070 private static void loge(String msg) { 1071 Log.e(TAG, msg); 1072 } 1073 1074 private static void loge(String msg, Throwable tr) { 1075 Log.e(TAG, msg, tr); 1076 } 1077 } 1078