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
17package com.android.omadm.service;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.SharedPreferences;
23import android.net.ConnectivityManager;
24import android.net.NetworkInfo;
25import android.net.wifi.WifiInfo;
26import android.net.wifi.WifiManager;
27import android.os.Bundle;
28import android.os.SystemProperties;
29import android.telephony.TelephonyManager;
30import android.text.TextUtils;
31import android.util.Log;
32
33import com.android.internal.telephony.Phone;
34import com.android.internal.telephony.TelephonyProperties;
35
36import java.io.File;
37import java.io.FileInputStream;
38import java.io.FileOutputStream;
39import java.io.IOException;
40import java.nio.charset.StandardCharsets;
41import java.text.SimpleDateFormat;
42import java.util.Date;
43import java.util.Locale;
44import java.util.TimeZone;
45
46public 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