• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
getInstance(Context context)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 
CapabilityPolling(Context context)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 
setRcsTestMode(boolean test)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 
initialise()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 
start()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 
stop()238     public void stop() {
239         cancelDiscoveryAlarm();
240         clearPollingTasks();
241         mContext.unregisterReceiver(mReceiver);
242         mDiscoveryThread.quit();
243     }
244 
registerForBroadcasts()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 
isPollingReady()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 
serviceStatusChanged(boolean enabled)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 
publishStateChanged(int state)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 
provisionStateChanged()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 
settingsChanged()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 
newContactAdded(String number)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 
verifyPollingResult(int counts)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 
schedulePolling(long msec, int type)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 
doCapabilityDiscovery(int type)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 
randomCapabilityPollInterval()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 
updateObsoleteItems()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 
cancelDiscoveryAlarm()648     private void cancelDiscoveryAlarm() {
649         if (mDiscoveryAlarmIntent != null) {
650             mAlarmManager.cancel(mDiscoveryAlarmIntent);
651             mDiscoveryAlarmIntent = null;
652             mNextPollingTimeStamp = 0L;
653         }
654     }
655 
clearPollingTasks()656     private void clearPollingTasks() {
657         PollingsQueue queue = PollingsQueue.getInstance(null);
658         if (queue != null) {
659             queue.clear();
660         }
661     }
662 
getTimeString(long time)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 
enqueueDiscovery(int type)682     public void enqueueDiscovery(int type) {
683         mDiscoveryHandler.removeMessages(MSG_CHECK_DISCOVERY);
684         mDiscoveryHandler.obtainMessage(MSG_CHECK_DISCOVERY, type, -1).sendToTarget();
685     }
686 
enqueueNewContact(String number)687     public void enqueueNewContact(String number) {
688         mDiscoveryHandler.obtainMessage(MSG_NEW_CONTACT_ADDED, number).sendToTarget();
689     }
690 
enqueueServiceStatusChanged(boolean enabled)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 
enqueuePublishStateChanged(int state)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 
enqueueSettingsChanged()703     public void enqueueSettingsChanged() {
704         mDiscoveryHandler.removeMessages(MSG_SETTINGS_CHANGED);
705         mDiscoveryHandler.obtainMessage(MSG_SETTINGS_CHANGED).sendToTarget();
706     }
707 
enqueueSimLoaded()708     private void enqueueSimLoaded() {
709         mDiscoveryHandler.removeMessages(MSG_SIM_LOADED);
710         mDiscoveryHandler.obtainMessage(MSG_SIM_LOADED).sendToTarget();
711     }
712 
enqueueProvisionStateChanged()713     private void enqueueProvisionStateChanged() {
714         mDiscoveryHandler.removeMessages(MSG_PROVISION_STATE_CHANGED);
715         mDiscoveryHandler.obtainMessage(MSG_PROVISION_STATE_CHANGED).sendToTarget();
716     }
717 
enqueueVerifyPollingResult(int counts)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 
onSimLoaded()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;
getLine1Number()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 
getSubscriberId()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