Home | History | Annotate | Download | only in presence
      1 /*
      2  * Copyright (c) 2015, Motorola Mobility LLC
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *     - Redistributions of source code must retain the above copyright
      8  *       notice, this list of conditions and the following disclaimer.
      9  *     - Redistributions in binary form must reproduce the above copyright
     10  *       notice, this list of conditions and the following disclaimer in the
     11  *       documentation and/or other materials provided with the distribution.
     12  *     - Neither the name of Motorola Mobility nor the
     13  *       names of its contributors may be used to endorse or promote products
     14  *       derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     26  * DAMAGE.
     27  */
     28 
     29 package com.android.service.ims.presence;
     30 
     31 import android.app.AlarmManager;
     32 import android.app.PendingIntent;
     33 import android.content.BroadcastReceiver;
     34 import android.content.Context;
     35 import android.content.Intent;
     36 import android.content.IntentFilter;
     37 import android.database.Cursor;
     38 import android.net.Uri;
     39 import android.os.Handler;
     40 import android.os.HandlerThread;
     41 import android.os.Message;
     42 import android.os.Process;
     43 import android.os.SystemClock;
     44 import android.telephony.PhoneNumberUtils;
     45 import android.telephony.SubscriptionManager;
     46 import android.telephony.TelephonyManager;
     47 import android.text.format.Time;
     48 import android.text.TextUtils;
     49 import android.content.ComponentName;
     50 
     51 import com.android.ims.ImsConfig;
     52 import com.android.ims.ImsException;
     53 import com.android.ims.ImsManager;
     54 import com.android.internal.telephony.IccCardConstants;
     55 import com.android.internal.telephony.TelephonyIntents;
     56 
     57 import com.android.ims.RcsException;
     58 import com.android.ims.RcsManager;
     59 import com.android.ims.RcsManager.ResultCode;
     60 import com.android.ims.RcsPresence;
     61 import com.android.ims.RcsPresence.PublishState;
     62 import com.android.ims.RcsPresenceInfo;
     63 import com.android.ims.internal.ContactNumberUtils;
     64 import com.android.ims.internal.Logger;
     65 
     66 import java.util.ArrayList;
     67 import java.util.List;
     68 
     69 public class CapabilityPolling {
     70     private Logger logger = Logger.getLogger(this.getClass().getName());
     71     private final Context mContext;
     72 
     73     public static final String ACTION_PERIODICAL_DISCOVERY_ALARM =
     74             "com.android.service.ims.presence.periodical_capability_discovery";
     75     private PendingIntent mDiscoveryAlarmIntent = null;
     76 
     77     public static final int ACTION_POLLING_NORMAL = 0;
     78     public static final int ACTION_POLLING_NEW_CONTACTS = 1;
     79 
     80     private long mCapabilityPollInterval = 604800000L;
     81     private long mMinCapabilityPollInterval = 60480000L;
     82     private long mCapabilityCacheExpiration = 7776000000L;
     83     private long mNextPollingTimeStamp = 0L;
     84     private final Object mScheduleSyncObj = new Object();
     85 
     86     private boolean mInitialized = false;
     87     private AlarmManager mAlarmManager = null;
     88     private EABContactManager mEABContactManager = null;
     89     private boolean mStackAvailable = false;
     90     private int mPublished = -1;
     91     private int mProvisioned = -1;
     92 
     93     private HandlerThread mDiscoveryThread;
     94     private Handler mDiscoveryHandler;
     95 
     96     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
     97         @Override
     98         public void onReceive(Context context, Intent intent) {
     99             logger.info("onReceive(), intent: " + intent +
    100                     ", context: " + context);
    101 
    102             String action = intent.getAction();
    103             if (RcsManager.ACTION_RCS_SERVICE_AVAILABLE.equals(action)) {
    104                 enqueueServiceStatusChanged(true);
    105             } else if (RcsManager.ACTION_RCS_SERVICE_UNAVAILABLE.equals(action)) {
    106                 enqueueServiceStatusChanged(false);
    107             } else if (RcsManager.ACTION_RCS_SERVICE_DIED.equals(action)) {
    108                 logger.warn("No handler for this intent: " + action);
    109             } else if (RcsPresence.ACTION_PUBLISH_STATE_CHANGED.equals(action)) {
    110                 int state = intent.getIntExtra(
    111                         RcsPresence.EXTRA_PUBLISH_STATE,
    112                         RcsPresence.PublishState.PUBLISH_STATE_NOT_PUBLISHED);
    113                 enqueuePublishStateChanged(state);
    114             } else if (ImsConfig.ACTION_IMS_CONFIG_CHANGED.equals(action)) {
    115                 int item = intent.getIntExtra(ImsConfig.EXTRA_CHANGED_ITEM, -1);
    116                 if ((ImsConfig.ConfigConstants.CAPABILITIES_POLL_INTERVAL == item) ||
    117                     (ImsConfig.ConfigConstants.CAPABILITIES_CACHE_EXPIRATION == item)) {
    118                     enqueueSettingsChanged();
    119                 } else if ((ImsConfig.ConfigConstants.VLT_SETTING_ENABLED == item) ||
    120                         (ImsConfig.ConfigConstants.LVC_SETTING_ENABLED == item) ||
    121                         (ImsConfig.ConfigConstants.EAB_SETTING_ENABLED == item)) {
    122                     enqueueProvisionStateChanged();
    123                 }
    124             } else if(TelephonyIntents.ACTION_SIM_STATE_CHANGED.equalsIgnoreCase(action)) {
    125                 String stateExtra = intent.getStringExtra(
    126                         IccCardConstants.INTENT_KEY_ICC_STATE);
    127                 logger.print("SIM_STATE_CHANGED: " + stateExtra);
    128                 if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equalsIgnoreCase(stateExtra)) {
    129                     enqueueSimLoaded();
    130                 }
    131             } else {
    132                 logger.debug("No interest in this intent: " + action);
    133             }
    134         }
    135     };
    136 
    137     private static CapabilityPolling sInstance = null;
    138     public static synchronized CapabilityPolling getInstance(Context context) {
    139         if ((sInstance == null) && (context != null)) {
    140             sInstance = new CapabilityPolling(context);
    141         }
    142 
    143         return sInstance;
    144     }
    145 
    146     private CapabilityPolling(Context context) {
    147         mContext = context;
    148 
    149         ContactNumberUtils.getDefault().setContext(mContext);
    150         PresencePreferences.getInstance().setContext(mContext);
    151 
    152         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
    153         mEABContactManager = new EABContactManager(mContext.getContentResolver(),
    154                 mContext.getPackageName());
    155 
    156         setRcsTestMode(PresencePreferences.getInstance().getRcsTestMode());
    157         logger.debug("CapabilityPolling is created.");
    158     }
    159 
    160     public void setRcsTestMode(boolean test) {
    161         logger.setRcsTestMode(test);
    162 
    163         PresencePreferences pref = PresencePreferences.getInstance();
    164         if (pref != null) {
    165             if (pref.getRcsTestMode() != test) {
    166                 pref.setRcsTestMode(test);
    167             }
    168         }
    169     }
    170 
    171     private void initialise() {
    172         if (mInitialized) {
    173             return;
    174         }
    175 
    176         PresenceSetting.init(mContext);
    177         long capabilityPollInterval = PresenceSetting.getCapabilityPollInterval();
    178         logger.print("getCapabilityPollInterval: " + capabilityPollInterval);
    179         if (capabilityPollInterval == -1) {
    180             capabilityPollInterval = mContext.getResources().getInteger(
    181                     R.integer.capability_poll_interval);
    182         }
    183         if (capabilityPollInterval < 10) {
    184             capabilityPollInterval = 10;
    185         }
    186 
    187         long capabilityCacheExpiration = PresenceSetting.getCapabilityCacheExpiration();
    188         logger.print("getCapabilityCacheExpiration: " + capabilityCacheExpiration);
    189         if (capabilityCacheExpiration == -1) {
    190             capabilityCacheExpiration = mContext.getResources().getInteger(
    191                     R.integer.capability_cache_expiration);
    192         }
    193         if (capabilityCacheExpiration < 10) {
    194             capabilityCacheExpiration = 10;
    195         }
    196 
    197         int publishTimer = PresenceSetting.getPublishTimer();
    198         logger.print("getPublishTimer: " + publishTimer);
    199         int publishTimerExtended = PresenceSetting.getPublishTimerExtended();
    200         logger.print("getPublishTimerExtended: " + publishTimerExtended);
    201         int maxEntriesInRequest = PresenceSetting.getMaxNumberOfEntriesInRequestContainedList();
    202         logger.print("getMaxNumberOfEntriesInRequestContainedList: " + maxEntriesInRequest);
    203         if ((capabilityPollInterval <= 30 * 60) ||     // default: 7 days
    204             (capabilityCacheExpiration <= 60 * 60) ||  // default: 90 days
    205             (maxEntriesInRequest <= 20) ||             // default: 100
    206             (publishTimer <= 10 * 60) ||               // default: 20 minutes
    207             (publishTimerExtended <= 20 * 60)) {       // default: 1 day
    208             setRcsTestMode(true);
    209         }
    210 
    211         if (capabilityCacheExpiration < capabilityPollInterval) {
    212             capabilityPollInterval = capabilityCacheExpiration;
    213         }
    214 
    215         mCapabilityPollInterval = capabilityPollInterval * 1000;
    216         mMinCapabilityPollInterval = mCapabilityPollInterval / 10;
    217         logger.info("mCapabilityPollInterval: " + mCapabilityPollInterval +
    218                 ", mMinCapabilityPollInterval: " + mMinCapabilityPollInterval);
    219 
    220         mCapabilityCacheExpiration = capabilityCacheExpiration * 1000;
    221         logger.info("mCapabilityCacheExpiration: " + mCapabilityCacheExpiration);
    222 
    223         mInitialized = true;
    224     }
    225 
    226     public void start() {
    227         mDiscoveryThread = new HandlerThread("Presence-DiscoveryThread");
    228         mDiscoveryThread.start();
    229         mDiscoveryHandler = new Handler(mDiscoveryThread.getLooper(), mDiscoveryCallback);
    230 
    231         registerForBroadcasts();
    232 
    233         if (isPollingReady()) {
    234             schedulePolling(5 * 1000, ACTION_POLLING_NORMAL);
    235         }
    236     }
    237 
    238     public void stop() {
    239         cancelDiscoveryAlarm();
    240         clearPollingTasks();
    241         mContext.unregisterReceiver(mReceiver);
    242         mDiscoveryThread.quit();
    243     }
    244 
    245     private void registerForBroadcasts() {
    246         IntentFilter intentFilter = new IntentFilter();
    247         intentFilter.addAction(RcsManager.ACTION_RCS_SERVICE_AVAILABLE);
    248         intentFilter.addAction(RcsManager.ACTION_RCS_SERVICE_UNAVAILABLE);
    249         intentFilter.addAction(RcsManager.ACTION_RCS_SERVICE_DIED);
    250         intentFilter.addAction(RcsPresence.ACTION_PUBLISH_STATE_CHANGED);
    251         intentFilter.addAction(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
    252         intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
    253         mContext.registerReceiver(mReceiver, intentFilter);
    254     }
    255 
    256     private boolean isPollingReady() {
    257         if (mPublished == -1) {
    258             RcsManager rcsManager = RcsManager.getInstance(mContext, 0);
    259             if (rcsManager != null) {
    260                 mStackAvailable = rcsManager.isRcsServiceAvailable();
    261                 logger.print("isPollingReady, mStackAvailable: " + mStackAvailable);
    262                 try {
    263                     RcsPresence rcsPresence = rcsManager.getRcsPresenceInterface();
    264                     if (rcsPresence != null) {
    265                         int state = rcsPresence.getPublishState();
    266                         mPublished = (PublishState.PUBLISH_STATE_200_OK == state) ? 1 : 0;
    267                         logger.print("isPollingReady, mPublished: " + mPublished);
    268                     }
    269                 } catch (RcsException ex) {
    270                     logger.warn("RcsPresence.getPublishState failed, exception: " + ex);
    271                     mPublished = -1;
    272                 }
    273             }
    274         }
    275 
    276         if (mProvisioned == -1) {
    277             ImsManager imsManager = ImsManager.getInstance(mContext,
    278                     SubscriptionManager.getDefaultVoicePhoneId());
    279             if (imsManager != null) {
    280                 try {
    281                     ImsConfig imsConfig = imsManager.getConfigInterface();
    282                     if (imsConfig != null) {
    283                         mProvisioned = imsConfig.getProvisionedValue(
    284                             ImsConfig.ConfigConstants.EAB_SETTING_ENABLED);
    285                         logger.print("isPollingReady, mProvisioned: " + mProvisioned);
    286                     }
    287                 } catch (ImsException ex) {
    288                     logger.warn("ImsConfig.getEabProvisioned failed, exception: " + ex);
    289                     mProvisioned = -1;
    290                 }
    291             }
    292         }
    293         logger.print("isPollingReady, mProvisioned: " + mProvisioned +
    294                 ", mStackAvailable: " + mStackAvailable + ", mPublished: " + mPublished);
    295         return mStackAvailable && (mPublished == 1) && (mProvisioned == 1);
    296     }
    297 
    298     private void serviceStatusChanged(boolean enabled) {
    299         logger.print("Enter serviceStatusChanged: " + enabled);
    300         mStackAvailable = enabled;
    301 
    302         if (isPollingReady()) {
    303             schedulePolling(0, ACTION_POLLING_NORMAL);
    304         } else {
    305             cancelDiscoveryAlarm();
    306             clearPollingTasks();
    307         }
    308     }
    309 
    310     private void publishStateChanged(int state) {
    311         logger.print("Enter publishStateChanged: " + state);
    312         mPublished = (PublishState.PUBLISH_STATE_200_OK == state) ? 1 : 0;
    313         if (mPublished == 1) {
    314             PresencePreferences pref = PresencePreferences.getInstance();
    315             if (pref != null) {
    316                 String mdn_old = pref.getLine1Number();
    317                 String subscriberId_old = pref.getSubscriberId();
    318                 if (TextUtils.isEmpty(mdn_old) && TextUtils.isEmpty(subscriberId_old)) {
    319                     String mdn = getLine1Number();
    320                     pref.setLine1Number(mdn);
    321                     String subscriberId = getSubscriberId();
    322                     pref.setSubscriberId(subscriberId);
    323                 }
    324             }
    325         }
    326 
    327         if (isPollingReady()) {
    328             schedulePolling(0, ACTION_POLLING_NORMAL);
    329         } else {
    330             cancelDiscoveryAlarm();
    331             clearPollingTasks();
    332         }
    333     }
    334 
    335     private void provisionStateChanged() {
    336         ImsManager imsManager = ImsManager.getInstance(mContext,
    337                 SubscriptionManager.getDefaultVoicePhoneId());
    338         if (imsManager == null) {
    339             return;
    340         }
    341 
    342         try {
    343             ImsConfig imsConfig = imsManager.getConfigInterface();
    344             if (imsConfig == null) {
    345                 return;
    346             }
    347             boolean volteProvision = imsConfig.getProvisionedValue(
    348                     ImsConfig.ConfigConstants.VLT_SETTING_ENABLED)
    349                     == ImsConfig.FeatureValueConstants.ON;
    350             boolean vtProvision = imsConfig.getProvisionedValue(
    351                     ImsConfig.ConfigConstants.LVC_SETTING_ENABLED)
    352                     == ImsConfig.FeatureValueConstants.ON;
    353             boolean eabProvision = imsConfig.getProvisionedValue(
    354                     ImsConfig.ConfigConstants.EAB_SETTING_ENABLED)
    355                     == ImsConfig.FeatureValueConstants.ON;
    356             logger.print("Provision state changed, VolteProvision: "
    357                         + volteProvision + ", vtProvision: " + vtProvision
    358                         + ", eabProvision: " + eabProvision);
    359 
    360             if ((mProvisioned == 1) && !eabProvision) {
    361                 logger.print("EAB Provision is disabled, clear all capabilities!");
    362                 if (mEABContactManager != null) {
    363                     mEABContactManager.updateAllCapabilityToUnknown();
    364                 }
    365             }
    366             mProvisioned = eabProvision ? 1 : 0;
    367         } catch (ImsException ex) {
    368             logger.warn("ImsConfig.getEabProvisioned failed, exception: " + ex);
    369             mProvisioned = -1;
    370             return;
    371         }
    372 
    373         if (isPollingReady()) {
    374             schedulePolling(0, ACTION_POLLING_NORMAL);
    375         } else {
    376             cancelDiscoveryAlarm();
    377             clearPollingTasks();
    378         }
    379     }
    380 
    381     private void settingsChanged() {
    382         logger.print("Enter settingsChanged.");
    383         cancelDiscoveryAlarm();
    384         clearPollingTasks();
    385         mInitialized = false;
    386 
    387         if (isPollingReady()) {
    388             schedulePolling(0, ACTION_POLLING_NORMAL);
    389         }
    390     }
    391 
    392     private void newContactAdded(String number) {
    393         if (TextUtils.isEmpty(number)) {
    394             return;
    395         }
    396 
    397         EABContactManager.Request request = new EABContactManager.Request(number)
    398                 .setLastUpdatedTimeStamp(0);
    399         int result = mEABContactManager.update(request);
    400         if (result <= 0) {
    401             return;
    402         }
    403 
    404         if (isPollingReady()) {
    405             schedulePolling(5 * 1000, ACTION_POLLING_NEW_CONTACTS);
    406         }
    407     }
    408 
    409     private void verifyPollingResult(int counts) {
    410         if (isPollingReady()) {
    411             PresencePreferences pref = PresencePreferences.getInstance();
    412             if ((pref != null) && pref.getRcsTestMode()) {
    413                 counts = 1;
    414             }
    415             long lm = (long)(1 << (counts - 1));
    416             schedulePolling(30 * 1000 * lm, ACTION_POLLING_NORMAL);
    417         }
    418     }
    419 
    420     public synchronized void schedulePolling(long msec, int type) {
    421         logger.print("schedulePolling msec=" + msec + " type=" + type);
    422         if (!isPollingReady()) {
    423             logger.debug("Cancel the polling since the network is not ready");
    424             return;
    425         }
    426 
    427         if (type == ACTION_POLLING_NEW_CONTACTS) {
    428             cancelDiscoveryAlarm();
    429         }
    430 
    431         if (mNextPollingTimeStamp != 0L) {
    432             long scheduled = mNextPollingTimeStamp - System.currentTimeMillis();
    433             if ((scheduled > 0) && (scheduled < msec)) {
    434                 logger.print("There has been a discovery scheduled at "
    435                         + getTimeString(mNextPollingTimeStamp));
    436                 return;
    437             }
    438         }
    439 
    440         long nextTime = System.currentTimeMillis() + msec;
    441         logger.print("A new discovery needs to be started in " +
    442                 (msec / 1000) + " seconds at "
    443                 + getTimeString(nextTime));
    444 
    445         cancelDiscoveryAlarm();
    446 
    447         if (msec <= 0) {
    448             enqueueDiscovery(type);
    449             return;
    450         }
    451 
    452         Intent intent = new Intent(ACTION_PERIODICAL_DISCOVERY_ALARM);
    453         intent.setClass(mContext, AlarmBroadcastReceiver.class);
    454         intent.putExtra("pollingType", type);
    455 
    456         mDiscoveryAlarmIntent = PendingIntent.getBroadcast(mContext, 0, intent,
    457                 PendingIntent.FLAG_UPDATE_CURRENT);
    458         mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    459                 SystemClock.elapsedRealtime() + msec, mDiscoveryAlarmIntent);
    460 
    461         mNextPollingTimeStamp = nextTime;
    462     }
    463 
    464     private synchronized void doCapabilityDiscovery(int type) {
    465         logger.print("doCapabilityDiscovery type=" + type);
    466         if (!isPollingReady()) {
    467             logger.debug("doCapabilityDiscovery isPollingReady=false");
    468             return;
    469         }
    470 
    471         long delay = 0;
    472         List<Contacts.Item> list = null;
    473 
    474         initialise();
    475 
    476         mNextPollingTimeStamp = 0L;
    477         Cursor cursor = null;
    478         EABContactManager.Query baseQuery = new EABContactManager.Query()
    479                 .orderBy(EABContactManager.COLUMN_LAST_UPDATED_TIMESTAMP,
    480                          EABContactManager.Query.ORDER_ASCENDING);
    481         try {
    482             logger.debug("doCapabilityDiscovery.query:\n" + baseQuery);
    483             cursor = mEABContactManager.query(baseQuery);
    484             if (cursor == null) {
    485                 logger.print("Cursor is null, there is no database found.");
    486                 return;
    487             }
    488             int count = cursor.getCount();
    489             if (count == 0) {
    490                 logger.print("Cursor.getCount() is 0, there is no items found in db.");
    491                 return;
    492             }
    493 
    494             list = new ArrayList<Contacts.Item>();
    495             list.clear();
    496 
    497             long current = System.currentTimeMillis();
    498             for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
    499                 long id = cursor.getLong(cursor.getColumnIndex(Contacts.Impl._ID));
    500                 long last = cursor.getLong(cursor.getColumnIndex(
    501                         Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP));
    502 
    503                 Contacts.Item item = new Contacts.Item(id);
    504                 item.setLastUpdateTime(last);
    505                 item.setNumber(cursor.getString(
    506                         cursor.getColumnIndex(Contacts.Impl.CONTACT_NUMBER)));
    507                 item.setName(cursor.getString(
    508                         cursor.getColumnIndex(Contacts.Impl.CONTACT_NAME)));
    509                 item.setVolteTimestamp(cursor.getLong(
    510                         cursor.getColumnIndex(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP)));
    511                 item.setVideoTimestamp(cursor.getLong(
    512                         cursor.getColumnIndex(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP)));
    513 
    514                 if ((current - last < 0) ||
    515                     (current - last >= mCapabilityPollInterval - mMinCapabilityPollInterval)) {
    516                     logger.print("This item will be updated:\n"
    517                             + item);
    518                     if (item.isValid()) {
    519                         list.add(item);
    520                     }
    521                 } else {
    522                     logger.print("The first item which will be updated next time:\n" + item);
    523                     delay = randomCapabilityPollInterval() - (current - last);
    524                     if (delay > 0) {
    525                         break;
    526                     }
    527                 }
    528             }
    529         } catch (Exception ex) {
    530             logger.warn("Exception in doCapabilityDiscovery: " + ex);
    531             if (delay <= 0) {
    532                 delay = 5 * 60 * 1000;
    533             }
    534         } finally {
    535             if (cursor != null) {
    536                 cursor.close();
    537             }
    538         }
    539 
    540         if (delay <= 0) {
    541             delay = randomCapabilityPollInterval();
    542         }
    543         logger.print("Polling delay: " + delay);
    544         schedulePolling(delay, ACTION_POLLING_NORMAL);
    545 
    546         updateObsoleteItems();
    547 
    548         if ((list != null) && (list.size() > 0)) {
    549             PollingsQueue queue = PollingsQueue.getInstance(mContext);
    550             if (queue != null) {
    551                 queue.setCapabilityPolling(this);
    552                 queue.add(type, list);
    553             }
    554         }
    555     }
    556 
    557     private long randomCapabilityPollInterval() {
    558         double random = Math.random() * 0.2 + 0.9;
    559         logger.print("The random for this time polling is: " + random);
    560         random = random * mCapabilityPollInterval;
    561         return (long)random;
    562     }
    563 
    564     private void updateObsoleteItems() {
    565         long current = System.currentTimeMillis();
    566         long last = current - mCapabilityCacheExpiration;
    567         long last3year = current - 3 * 365 * 24 * 3600000L;
    568         StringBuilder sb = new StringBuilder();
    569         sb.append("((");
    570         sb.append(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP + "<='" + last + "'");
    571         sb.append(" OR ");
    572         sb.append(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP + "<='" + last + "'");
    573         sb.append(") AND ");
    574         sb.append(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP + ">='" + last3year + "'");
    575         sb.append(")");
    576         EABContactManager.Query baseQuery = new EABContactManager.Query()
    577                 .setFilterByTime(sb.toString())
    578                 .orderBy(EABContactManager.COLUMN_ID,
    579                          EABContactManager.Query.ORDER_ASCENDING);
    580 
    581         Cursor cursor = null;
    582         List<Contacts.Item> list = null;
    583         try {
    584             logger.debug("updateObsoleteItems.query:\n" + baseQuery);
    585             cursor = mEABContactManager.query(baseQuery);
    586             if (cursor == null) {
    587                 logger.print("Cursor is null, there is no database found.");
    588                 return;
    589             }
    590             int count = cursor.getCount();
    591             if (count == 0) {
    592                 logger.print("Cursor.getCount() is 0, there is no obsolete items found.");
    593                 return;
    594             }
    595 
    596             list = new ArrayList<Contacts.Item>();
    597             list.clear();
    598 
    599             for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
    600                 long id = cursor.getLong(cursor.getColumnIndex(Contacts.Impl._ID));
    601 
    602                 Contacts.Item item = new Contacts.Item(id);
    603                 item.setLastUpdateTime(cursor.getLong(
    604                         cursor.getColumnIndex(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP)));
    605                 item.setNumber(cursor.getString(
    606                         cursor.getColumnIndex(Contacts.Impl.CONTACT_NUMBER)));
    607                 item.setName(cursor.getString(
    608                         cursor.getColumnIndex(Contacts.Impl.CONTACT_NAME)));
    609                 item.setVolteTimestamp(cursor.getLong(
    610                         cursor.getColumnIndex(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP)));
    611                 item.setVideoTimestamp(cursor.getLong(
    612                         cursor.getColumnIndex(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP)));
    613                 logger.print("updateObsoleteItems, the obsolete item:\n" + item);
    614 
    615                 if ((item.lastUpdateTime() > 0) && item.isValid()) {
    616                     list.add(item);
    617                 }
    618             }
    619         } catch (Exception ex) {
    620             logger.warn("Exception in updateObsoleteItems: " + ex);
    621         } finally {
    622             if (cursor != null) {
    623                 cursor.close();
    624             }
    625         }
    626 
    627         if ((list == null) || (list.size() <= 0)) {
    628             return;
    629         }
    630 
    631         for (Contacts.Item item : list) {
    632             EABContactManager.Request request = new EABContactManager.Request(item.id());
    633             if (item.volteTimestamp() <= last) {
    634                 request.setVolteCallCapability(false);
    635                 request.setVolteCallCapabilityTimeStamp(current);
    636             }
    637             if (item.videoTimestamp() <= last) {
    638                 request.setVideoCallCapability(false);
    639                 request.setVideoCallCapabilityTimeStamp(current);
    640             }
    641             int result = mEABContactManager.update(request);
    642             if (result <= 0) {
    643                 logger.print("Failed to update this request: " + request);
    644             }
    645         }
    646     }
    647 
    648     private void cancelDiscoveryAlarm() {
    649         if (mDiscoveryAlarmIntent != null) {
    650             mAlarmManager.cancel(mDiscoveryAlarmIntent);
    651             mDiscoveryAlarmIntent = null;
    652             mNextPollingTimeStamp = 0L;
    653         }
    654     }
    655 
    656     private void clearPollingTasks() {
    657         PollingsQueue queue = PollingsQueue.getInstance(null);
    658         if (queue != null) {
    659             queue.clear();
    660         }
    661     }
    662 
    663     private String getTimeString(long time) {
    664         if (time <= 0) {
    665             time = System.currentTimeMillis();
    666         }
    667 
    668         Time tobj = new Time();
    669         tobj.set(time);
    670         return String.format("%s.%s", tobj.format("%m-%d %H:%M:%S"), time % 1000);
    671     }
    672 
    673     private static final int MSG_CHECK_DISCOVERY = 1;
    674     private static final int MSG_NEW_CONTACT_ADDED = 2;
    675     private static final int MSG_SERVICE_STATUS_CHANGED = 3;
    676     private static final int MSG_SETTINGS_CHANGED = 4;
    677     private static final int MSG_PUBLISH_STATE_CHANGED = 5;
    678     private static final int MSG_SIM_LOADED = 6;
    679     private static final int MSG_PROVISION_STATE_CHANGED = 7;
    680     private static final int MSG_VERIFY_POLLING_RESULT = 8;
    681 
    682     public void enqueueDiscovery(int type) {
    683         mDiscoveryHandler.removeMessages(MSG_CHECK_DISCOVERY);
    684         mDiscoveryHandler.obtainMessage(MSG_CHECK_DISCOVERY, type, -1).sendToTarget();
    685     }
    686 
    687     public void enqueueNewContact(String number) {
    688         mDiscoveryHandler.obtainMessage(MSG_NEW_CONTACT_ADDED, number).sendToTarget();
    689     }
    690 
    691     private void enqueueServiceStatusChanged(boolean enabled) {
    692         mDiscoveryHandler.removeMessages(MSG_SERVICE_STATUS_CHANGED);
    693         mDiscoveryHandler.obtainMessage(MSG_SERVICE_STATUS_CHANGED,
    694                 enabled ? 1 : 0, -1).sendToTarget();
    695     }
    696 
    697     private void enqueuePublishStateChanged(int state) {
    698         mDiscoveryHandler.removeMessages(MSG_PUBLISH_STATE_CHANGED);
    699         mDiscoveryHandler.obtainMessage(MSG_PUBLISH_STATE_CHANGED,
    700                 state, -1).sendToTarget();
    701     }
    702 
    703     public void enqueueSettingsChanged() {
    704         mDiscoveryHandler.removeMessages(MSG_SETTINGS_CHANGED);
    705         mDiscoveryHandler.obtainMessage(MSG_SETTINGS_CHANGED).sendToTarget();
    706     }
    707 
    708     private void enqueueSimLoaded() {
    709         mDiscoveryHandler.removeMessages(MSG_SIM_LOADED);
    710         mDiscoveryHandler.obtainMessage(MSG_SIM_LOADED).sendToTarget();
    711     }
    712 
    713     private void enqueueProvisionStateChanged() {
    714         mDiscoveryHandler.removeMessages(MSG_PROVISION_STATE_CHANGED);
    715         mDiscoveryHandler.obtainMessage(MSG_PROVISION_STATE_CHANGED).sendToTarget();
    716     }
    717 
    718     public void enqueueVerifyPollingResult(int counts) {
    719         mDiscoveryHandler.removeMessages(MSG_VERIFY_POLLING_RESULT);
    720         mDiscoveryHandler.obtainMessage(MSG_VERIFY_POLLING_RESULT, counts, -1).sendToTarget();
    721     }
    722 
    723     private Handler.Callback mDiscoveryCallback = new Handler.Callback() {
    724         @Override
    725         public boolean handleMessage(Message msg) {
    726             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    727 
    728             if (msg.what == MSG_CHECK_DISCOVERY) {
    729                 doCapabilityDiscovery(msg.arg1);
    730             } else if (msg.what == MSG_NEW_CONTACT_ADDED) {
    731                 newContactAdded((String)msg.obj);
    732             } else if (msg.what == MSG_SERVICE_STATUS_CHANGED) {
    733                 serviceStatusChanged(msg.arg1 == 1);
    734             } else if (msg.what == MSG_PUBLISH_STATE_CHANGED) {
    735                 publishStateChanged(msg.arg1);
    736             } else if (msg.what == MSG_SETTINGS_CHANGED) {
    737                 settingsChanged();
    738             } else if(msg.what == MSG_SIM_LOADED) {
    739                 onSimLoaded();
    740             } else if(msg.what == MSG_PROVISION_STATE_CHANGED) {
    741                 provisionStateChanged();
    742             } else if(msg.what == MSG_VERIFY_POLLING_RESULT) {
    743                 verifyPollingResult(msg.arg1);
    744             } else {
    745             }
    746 
    747             return true;
    748         }
    749     };
    750 
    751     private void onSimLoaded() {
    752         PresencePreferences pref = PresencePreferences.getInstance();
    753         if (pref == null) {
    754             return;
    755         }
    756 
    757         String mdn_old = pref.getLine1Number();
    758         if (TextUtils.isEmpty(mdn_old)) {
    759             return;
    760         }
    761         String subscriberId_old = pref.getSubscriberId();
    762         if (TextUtils.isEmpty(subscriberId_old)) {
    763             return;
    764         }
    765 
    766         String mdn = getLine1Number();
    767         String subscriberId = getSubscriberId();
    768         if (TextUtils.isEmpty(mdn) && TextUtils.isEmpty(subscriberId)) {
    769             return;
    770         }
    771 
    772         boolean mdnMatched = false;
    773         if (TextUtils.isEmpty(mdn) || PhoneNumberUtils.compare(mdn_old, mdn)) {
    774             mdnMatched = true;
    775         }
    776         boolean subscriberIdMatched = false;
    777         if (TextUtils.isEmpty(subscriberId) || subscriberId.equals(subscriberId_old)) {
    778             subscriberIdMatched = true;
    779         }
    780         if (mdnMatched && subscriberIdMatched) {
    781             return;
    782         }
    783 
    784         logger.print("Remove presence cache for Sim card changed!");
    785         pref.setLine1Number("");
    786         pref.setSubscriberId("");
    787 
    788         if (mEABContactManager != null) {
    789             mEABContactManager.updateAllCapabilityToUnknown();
    790         }
    791     }
    792 
    793     private static final int DEFAULT_SUBSCRIPTION = 1;
    794     private String getLine1Number() {
    795         if (mContext == null) {
    796             return null;
    797         }
    798 
    799         TelephonyManager telephony = (TelephonyManager)
    800                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
    801         if (telephony == null) {
    802             return null;
    803         }
    804 
    805         String mdn = null;
    806         if (TelephonyManager.getDefault().isMultiSimEnabled()) {
    807             int subId = SubscriptionManager.getDefaultDataSubscriptionId();
    808             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
    809                 subId = DEFAULT_SUBSCRIPTION;
    810             }
    811             mdn = telephony.getLine1Number(subId);
    812         } else {
    813             mdn = telephony.getLine1Number();
    814         }
    815 
    816         if ((mdn == null) || (mdn.length() == 0) ||  mdn.startsWith("00000")) {
    817             return null;
    818         }
    819 
    820         logger.print("getLine1Number: " + mdn);
    821         return mdn;
    822     }
    823 
    824     private String getSubscriberId() {
    825         if (mContext == null) {
    826             return null;
    827         }
    828 
    829         TelephonyManager telephony = (TelephonyManager)
    830                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
    831         if (telephony == null) {
    832             return null;
    833         }
    834 
    835         String subscriberId = null;
    836         if (TelephonyManager.getDefault().isMultiSimEnabled()) {
    837             int subId = SubscriptionManager.getDefaultDataSubscriptionId();
    838             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
    839                 subId = DEFAULT_SUBSCRIPTION;
    840             }
    841             subscriberId = telephony.getSubscriberId(subId);
    842         } else {
    843             subscriberId = telephony.getSubscriberId();
    844         }
    845 
    846         logger.print("getSubscriberId: " + subscriberId);
    847         return subscriberId;
    848     }
    849 }
    850