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.ComponentName;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.IntentFilter;
38 import android.database.Cursor;
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.telephony.ims.ImsException;
48 import android.telephony.ims.ImsManager;
49 import android.telephony.ims.ProvisioningManager;
50 import android.telephony.ims.RcsUceAdapter;
51 import android.text.format.TimeMigrationUtils;
52 import android.text.TextUtils;
53 
54 import com.android.ims.RcsException;
55 import com.android.ims.RcsManager;
56 import com.android.ims.RcsPresence;
57 import com.android.ims.RcsPresence.PublishState;
58 import com.android.ims.internal.ContactNumberUtils;
59 import com.android.ims.internal.Logger;
60 
61 import java.util.ArrayList;
62 import java.util.List;
63 
64 public class CapabilityPolling {
65     private Logger logger = Logger.getLogger(this.getClass().getName());
66     private final Context mContext;
67 
68     private static final String PERSIST_SERVICE_NAME =
69         "com.android.service.ims.presence.PersistService";
70     private static final String PERSIST_SERVICE_PACKAGE = "com.android.service.ims.presence";
71 
72     public static final String ACTION_PERIODICAL_DISCOVERY_ALARM =
73             "com.android.service.ims.presence.periodical_capability_discovery";
74     private PendingIntent mDiscoveryAlarmIntent = null;
75 
76     public static final int ACTION_POLLING_NORMAL = 0;
77     public static final int ACTION_POLLING_NEW_CONTACTS = 1;
78 
79     public static final int DELAY_REGISTER_CALLBACK_MS = 5000;
80 
81     private long mCapabilityPollInterval = 604800000L;
82     private long mMinCapabilityPollInterval = 60480000L;
83     private long mCapabilityCacheExpiration = 7776000000L;
84     private long mNextPollingTimeStamp = 0L;
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     private int mDefaultSubId;
93     private boolean isInitializing = false;
94 
95     private HandlerThread mDiscoveryThread;
96     private Handler mDiscoveryHandler;
97 
98     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
99         @Override
100         public void onReceive(Context context, Intent intent) {
101             logger.info("onReceive(), intent: " + intent +
102                     ", context: " + context);
103 
104             String action = intent.getAction();
105             if (RcsManager.ACTION_RCS_SERVICE_AVAILABLE.equals(action)) {
106                 enqueueServiceStatusChanged(true);
107             } else if (RcsManager.ACTION_RCS_SERVICE_UNAVAILABLE.equals(action)) {
108                 enqueueServiceStatusChanged(false);
109             } else if (RcsManager.ACTION_RCS_SERVICE_DIED.equals(action)) {
110                 logger.warn("No handler for this intent: " + action);
111             } else if (RcsPresence.ACTION_PUBLISH_STATE_CHANGED.equals(action)) {
112                 int state = intent.getIntExtra(
113                         RcsPresence.EXTRA_PUBLISH_STATE,
114                         RcsPresence.PublishState.PUBLISH_STATE_NOT_PUBLISHED);
115                 enqueuePublishStateChanged(state);
116             } else {
117                 logger.debug("No interest in this intent: " + action);
118             }
119         }
120     };
121 
122     private ProvisioningManager.Callback mProvisioningManagerCallback =
123             new ProvisioningManager.Callback() {
124         @Override
125         public void onProvisioningIntChanged(int item, int value) {
126             if ((ProvisioningManager.KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC == item) ||
127                     (ProvisioningManager.KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC == item)) {
128                 enqueueSettingsChanged();
129             } else if ((ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS == item) ||
130                     (ProvisioningManager.KEY_VT_PROVISIONING_STATUS == item) ||
131                     (ProvisioningManager.KEY_EAB_PROVISIONING_STATUS == item)) {
132                 enqueueProvisionStateChanged();
133             }
134         }
135     };
136 
137     private RcsUceAdapter.OnPublishStateChangedListener mPublishStateCallback =
138             new RcsUceAdapter.OnPublishStateChangedListener() {
139         @Override
140         public void onPublishStateChange(int publishState) {
141             logger.info("publish state changed: " + publishState);
142             Intent intent = new Intent(RcsPresence.ACTION_PUBLISH_STATE_CHANGED);
143             intent.putExtra(RcsPresence.EXTRA_PUBLISH_STATE, publishState);
144             mContext.sendStickyBroadcast(intent);
145             launchPersistService(intent);
146         }
147     };
148 
launchPersistService(Intent intent)149     private void launchPersistService(Intent intent) {
150         ComponentName component = new ComponentName(PERSIST_SERVICE_PACKAGE,
151             PERSIST_SERVICE_NAME);
152         intent.setComponent(component);
153         mContext.startService(intent);
154     }
155 
156     private Runnable mRegisterCallbackRunnable = this::tryProvisioningManagerRegistration;
157 
158     private static CapabilityPolling sInstance = null;
getInstance(Context context)159     public static synchronized CapabilityPolling getInstance(Context context) {
160         if ((sInstance == null) && (context != null)) {
161             sInstance = new CapabilityPolling(context);
162         }
163 
164         return sInstance;
165     }
166 
CapabilityPolling(Context context)167     private CapabilityPolling(Context context) {
168         mContext = context;
169 
170         ContactNumberUtils.getDefault().setContext(mContext);
171         PresencePreferences.getInstance().setContext(mContext);
172 
173         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
174         mEABContactManager = new EABContactManager(mContext.getContentResolver(),
175                 mContext.getPackageName());
176 
177         setRcsTestMode(PresencePreferences.getInstance().getRcsTestMode());
178         logger.debug("CapabilityPolling is created.");
179     }
180 
setRcsTestMode(boolean test)181     public void setRcsTestMode(boolean test) {
182         logger.setRcsTestMode(test);
183 
184         PresencePreferences pref = PresencePreferences.getInstance();
185         if (pref != null) {
186             if (pref.getRcsTestMode() != test) {
187                 pref.setRcsTestMode(test);
188             }
189         }
190     }
191 
initialise()192     private void initialise() {
193         if (mInitialized) {
194             return;
195         }
196         long capabilityPollInterval = PresenceSetting.getCapabilityPollInterval(mDefaultSubId);
197         logger.print("getCapabilityPollInterval: " + capabilityPollInterval);
198         if (capabilityPollInterval == -1) {
199             capabilityPollInterval = mContext.getResources().getInteger(
200                     R.integer.capability_poll_interval);
201         }
202         if (capabilityPollInterval < 10) {
203             capabilityPollInterval = 10;
204         }
205 
206         long capabilityCacheExpiration =
207                 PresenceSetting.getCapabilityCacheExpiration(mDefaultSubId);
208         logger.print("getCapabilityCacheExpiration: " + capabilityCacheExpiration);
209         if (capabilityCacheExpiration == -1) {
210             capabilityCacheExpiration = mContext.getResources().getInteger(
211                     R.integer.capability_cache_expiration);
212         }
213         if (capabilityCacheExpiration < 10) {
214             capabilityCacheExpiration = 10;
215         }
216 
217         int publishTimer = PresenceSetting.getPublishTimer(mDefaultSubId);
218         logger.print("getPublishTimer: " + publishTimer);
219         int publishTimerExtended = PresenceSetting.getPublishTimerExtended(mDefaultSubId);
220         logger.print("getPublishTimerExtended: " + publishTimerExtended);
221         int maxEntriesInRequest =
222                 PresenceSetting.getMaxNumberOfEntriesInRequestContainedList(mDefaultSubId);
223         logger.print("getMaxNumberOfEntriesInRequestContainedList: " + maxEntriesInRequest);
224         if ((capabilityPollInterval <= 30 * 60) ||     // default: 7 days
225             (capabilityCacheExpiration <= 60 * 60) ||  // default: 90 days
226             (maxEntriesInRequest <= 20) ||             // default: 100
227             (publishTimer <= 10 * 60) ||               // default: 20 minutes
228             (publishTimerExtended <= 20 * 60)) {       // default: 1 day
229             setRcsTestMode(true);
230         }
231 
232         if (capabilityCacheExpiration < capabilityPollInterval) {
233             capabilityPollInterval = capabilityCacheExpiration;
234         }
235 
236         mCapabilityPollInterval = capabilityPollInterval * 1000;
237         mMinCapabilityPollInterval = mCapabilityPollInterval / 10;
238         logger.info("mCapabilityPollInterval: " + mCapabilityPollInterval +
239                 ", mMinCapabilityPollInterval: " + mMinCapabilityPollInterval);
240 
241         mCapabilityCacheExpiration = capabilityCacheExpiration * 1000;
242         logger.info("mCapabilityCacheExpiration: " + mCapabilityCacheExpiration);
243 
244         mInitialized = true;
245     }
246 
start()247     public void start() {
248         mDiscoveryThread = new HandlerThread("Presence-DiscoveryThread");
249         mDiscoveryThread.start();
250         mDiscoveryHandler = new Handler(mDiscoveryThread.getLooper(), mDiscoveryCallback);
251         mDefaultSubId = PresenceSetting.getDefaultSubscriptionId();
252 
253         registerForBroadcasts();
254 
255         if (isPollingReady()) {
256             schedulePolling(5 * 1000, ACTION_POLLING_NORMAL);
257         }
258     }
259 
stop()260     public void stop() {
261         cancelDiscoveryAlarm();
262         clearPollingTasks();
263         mContext.unregisterReceiver(mReceiver);
264         unregisterPublishStateChangedCallback();
265         mDiscoveryThread.quit();
266 
267         if (SubscriptionManager.isValidSubscriptionId(mDefaultSubId)) {
268             ProvisioningManager pm = ProvisioningManager.createForSubscriptionId(mDefaultSubId);
269             pm.unregisterProvisioningChangedCallback(mProvisioningManagerCallback);
270         }
271         mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
272     }
273 
registerForBroadcasts()274     private void registerForBroadcasts() {
275         IntentFilter intentFilter = new IntentFilter();
276         intentFilter.addAction(RcsManager.ACTION_RCS_SERVICE_AVAILABLE);
277         intentFilter.addAction(RcsManager.ACTION_RCS_SERVICE_UNAVAILABLE);
278         intentFilter.addAction(RcsManager.ACTION_RCS_SERVICE_DIED);
279         intentFilter.addAction(RcsPresence.ACTION_PUBLISH_STATE_CHANGED);
280         intentFilter.addAction(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
281         mContext.registerReceiver(mReceiver, intentFilter);
282     }
283 
registerPublishStateChangedCallback()284     private void registerPublishStateChangedCallback() {
285         try {
286             ImsManager imsManager =
287                     (ImsManager) mContext.getSystemService(Context.TELEPHONY_IMS_SERVICE);
288             RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(mDefaultSubId).getUceAdapter();
289             uceAdapter.addOnPublishStateChangedListener(mContext.getMainExecutor(),
290                     mPublishStateCallback);
291         } catch (Exception ex) {
292             logger.warn("register publish state callback failed, exception: " + ex);
293         }
294     }
295 
unregisterPublishStateChangedCallback()296     private void unregisterPublishStateChangedCallback() {
297         try {
298             ImsManager imsManager =
299                     (ImsManager) mContext.getSystemService(Context.TELEPHONY_IMS_SERVICE);
300             RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(mDefaultSubId).getUceAdapter();
301             uceAdapter.removeOnPublishStateChangedListener(mPublishStateCallback);
302         } catch (Exception ex) {
303             logger.warn("unregister publish state callback failed, exception: " + ex);
304         }
305     }
306 
isPollingReady()307     private boolean isPollingReady() {
308         RcsManager rcsManager = RcsManager.getInstance(mContext, 0);
309         if (rcsManager != null) {
310             mStackAvailable = rcsManager.isRcsServiceAvailable();
311             logger.print("isPollingReady, mStackAvailable: " + mStackAvailable);
312             try {
313                 RcsPresence rcsPresence = rcsManager.getRcsPresenceInterface();
314                 if (rcsPresence != null) {
315                     int state = rcsPresence.getPublishState();
316                     mPublished = (PublishState.PUBLISH_STATE_200_OK == state) ? 1 : 0;
317                     logger.print("isPollingReady, mPublished: " + mPublished);
318                 }
319             } catch (RcsException ex) {
320                 logger.warn("RcsPresence.getPublishState failed, exception: " + ex);
321                 mPublished = -1;
322             }
323         }
324 
325         try {
326             mProvisioned = PresenceSetting.getEabProvisioningConfig(mDefaultSubId);
327         } catch (Exception e) {
328             logger.warn("isPollingReady, couldn't get ProvisioningManager, exception="
329                     + e.getMessage());
330             mProvisioned = -1;
331         }
332 
333         logger.print("isPollingReady, mProvisioned: " + mProvisioned +
334                 ", mStackAvailable: " + mStackAvailable + ", mPublished: " + mPublished);
335         return mStackAvailable && (mPublished == 1) && (mProvisioned == 1);
336     }
337 
serviceStatusChanged(boolean enabled)338     private void serviceStatusChanged(boolean enabled) {
339         logger.print("Enter serviceStatusChanged: " + enabled);
340         mStackAvailable = enabled;
341 
342         if (isPollingReady()) {
343             schedulePolling(0, ACTION_POLLING_NORMAL);
344         } else {
345             cancelDiscoveryAlarm();
346             clearPollingTasks();
347         }
348     }
349 
publishStateChanged(int state)350     private void publishStateChanged(int state) {
351         logger.print("Enter publishStateChanged: " + state);
352         mPublished = (PublishState.PUBLISH_STATE_200_OK == state) ? 1 : 0;
353         if (mPublished == 1) {
354             PresencePreferences pref = PresencePreferences.getInstance();
355             if (pref != null) {
356                 String mdn_old = pref.getLine1Number();
357                 String subscriberId_old = pref.getSubscriberId();
358                 if (TextUtils.isEmpty(mdn_old) && TextUtils.isEmpty(subscriberId_old)) {
359                     String mdn = getLine1Number();
360                     pref.setLine1Number(mdn);
361                     String subscriberId = getSubscriberId();
362                     pref.setSubscriberId(subscriberId);
363                 }
364             }
365         }
366 
367         if (isPollingReady()) {
368             schedulePolling(0, ACTION_POLLING_NORMAL);
369         } else {
370             cancelDiscoveryAlarm();
371             clearPollingTasks();
372         }
373     }
374 
provisionStateChanged()375     private void provisionStateChanged() {
376         boolean volteProvisioned = PresenceSetting.getVoLteProvisioningConfig(mDefaultSubId) ==
377                 ProvisioningManager.PROVISIONING_VALUE_ENABLED;
378         boolean vtProvisioned = PresenceSetting.getVtProvisioningConfig(mDefaultSubId) ==
379                 ProvisioningManager.PROVISIONING_VALUE_ENABLED;
380         boolean eabProvisioned = PresenceSetting.getEabProvisioningConfig(mDefaultSubId) ==
381                 ProvisioningManager.PROVISIONING_VALUE_ENABLED;
382         logger.print("Provision state changed, VolteProvision: "
383                 + volteProvisioned + ", vtProvision: " + vtProvisioned
384                 + ", eabProvision: " + eabProvisioned);
385         if ((mProvisioned == 1) && !eabProvisioned) {
386             logger.print("EAB Provision is disabled, clear all capabilities!");
387             if (mEABContactManager != null) {
388                 mEABContactManager.updateAllCapabilityToUnknown();
389             }
390         }
391         mProvisioned = eabProvisioned ? 1 : 0;
392 
393         if (isPollingReady()) {
394             schedulePolling(0, ACTION_POLLING_NORMAL);
395         } else {
396             cancelDiscoveryAlarm();
397             clearPollingTasks();
398         }
399     }
400 
settingsChanged()401     private void settingsChanged() {
402         logger.print("Enter settingsChanged.");
403         cancelDiscoveryAlarm();
404         clearPollingTasks();
405         mInitialized = false;
406 
407         if (isPollingReady()) {
408             schedulePolling(0, ACTION_POLLING_NORMAL);
409         }
410     }
411 
newContactAdded(String number)412     private void newContactAdded(String number) {
413         if (TextUtils.isEmpty(number)) {
414             return;
415         }
416 
417         EABContactManager.Request request = new EABContactManager.Request(number)
418                 .setLastUpdatedTimeStamp(0);
419         int result = mEABContactManager.update(request);
420         if (result <= 0) {
421             return;
422         }
423 
424         if (isPollingReady()) {
425             schedulePolling(5 * 1000, ACTION_POLLING_NEW_CONTACTS);
426         }
427     }
428 
verifyPollingResult(int counts)429     private void verifyPollingResult(int counts) {
430         if (isPollingReady()) {
431             PresencePreferences pref = PresencePreferences.getInstance();
432             if ((pref != null) && pref.getRcsTestMode()) {
433                 counts = 1;
434             }
435             long lm = (long)(1 << (counts - 1));
436             schedulePolling(30 * 1000 * lm, ACTION_POLLING_NORMAL);
437         }
438     }
439 
schedulePolling(long msec, int type)440     public synchronized void schedulePolling(long msec, int type) {
441         logger.print("schedulePolling msec=" + msec + " type=" + type);
442         if (!isPollingReady()) {
443             logger.debug("Cancel the polling since the network is not ready");
444             return;
445         }
446 
447         if (type == ACTION_POLLING_NEW_CONTACTS) {
448             cancelDiscoveryAlarm();
449         }
450 
451         if (mNextPollingTimeStamp != 0L) {
452             long scheduled = mNextPollingTimeStamp - System.currentTimeMillis();
453             if ((scheduled > 0) && (scheduled < msec)) {
454                 logger.print("There has been a discovery scheduled at "
455                         + getTimeString(mNextPollingTimeStamp));
456                 return;
457             }
458         }
459 
460         long nextTime = System.currentTimeMillis() + msec;
461         logger.print("A new discovery needs to be started in " +
462                 (msec / 1000) + " seconds at "
463                 + getTimeString(nextTime));
464 
465         cancelDiscoveryAlarm();
466 
467         if (msec <= 0) {
468             enqueueDiscovery(type);
469             return;
470         }
471 
472         Intent intent = new Intent(ACTION_PERIODICAL_DISCOVERY_ALARM);
473         intent.setClass(mContext, AlarmBroadcastReceiver.class);
474         intent.putExtra("pollingType", type);
475 
476         mDiscoveryAlarmIntent = PendingIntent.getBroadcast(mContext, 0, intent,
477                 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
478         mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
479                 SystemClock.elapsedRealtime() + msec, mDiscoveryAlarmIntent);
480 
481         mNextPollingTimeStamp = nextTime;
482     }
483 
doCapabilityDiscovery(int type)484     private synchronized void doCapabilityDiscovery(int type) {
485         logger.print("doCapabilityDiscovery type=" + type);
486         if (!isPollingReady()) {
487             logger.debug("doCapabilityDiscovery isPollingReady=false");
488             return;
489         }
490 
491         long delay = 0;
492         List<Contacts.Item> list = null;
493 
494         initialise();
495 
496         mNextPollingTimeStamp = 0L;
497         Cursor cursor = null;
498         EABContactManager.Query baseQuery = new EABContactManager.Query()
499                 .orderBy(EABContactManager.COLUMN_LAST_UPDATED_TIMESTAMP,
500                          EABContactManager.Query.ORDER_ASCENDING);
501         try {
502             logger.debug("doCapabilityDiscovery.query:\n" + baseQuery);
503             cursor = mEABContactManager.query(baseQuery);
504             if (cursor == null) {
505                 logger.print("Cursor is null, there is no database found.");
506                 return;
507             }
508             int count = cursor.getCount();
509             if (count == 0) {
510                 logger.print("Cursor.getCount() is 0, there is no items found in db.");
511                 return;
512             }
513 
514             list = new ArrayList<Contacts.Item>();
515             list.clear();
516 
517             long current = System.currentTimeMillis();
518             for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
519                 long id = cursor.getLong(cursor.getColumnIndex(Contacts.Impl._ID));
520                 long last = cursor.getLong(cursor.getColumnIndex(
521                         Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP));
522 
523                 Contacts.Item item = new Contacts.Item(id);
524                 item.setLastUpdateTime(last);
525                 item.setNumber(cursor.getString(
526                         cursor.getColumnIndex(Contacts.Impl.CONTACT_NUMBER)));
527                 item.setName(cursor.getString(
528                         cursor.getColumnIndex(Contacts.Impl.CONTACT_NAME)));
529                 item.setVolteTimestamp(cursor.getLong(
530                         cursor.getColumnIndex(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP)));
531                 item.setVideoTimestamp(cursor.getLong(
532                         cursor.getColumnIndex(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP)));
533 
534                 if ((current - last < 0) ||
535                     (current - last >= mCapabilityPollInterval - mMinCapabilityPollInterval)) {
536                     logger.print("This item will be updated:\n"
537                             + item);
538                     if (item.isValid()) {
539                         list.add(item);
540                     }
541                 } else {
542                     logger.print("The first item which will be updated next time:\n" + item);
543                     delay = randomCapabilityPollInterval() - (current - last);
544                     if (delay > 0) {
545                         break;
546                     }
547                 }
548             }
549         } catch (Exception ex) {
550             logger.warn("Exception in doCapabilityDiscovery: " + ex);
551             if (delay <= 0) {
552                 delay = 5 * 60 * 1000;
553             }
554         } finally {
555             if (cursor != null) {
556                 cursor.close();
557             }
558         }
559 
560         if (delay <= 0) {
561             delay = randomCapabilityPollInterval();
562         }
563         logger.print("Polling delay: " + delay);
564         schedulePolling(delay, ACTION_POLLING_NORMAL);
565 
566         updateObsoleteItems();
567 
568         if ((list != null) && (list.size() > 0)) {
569             PollingsQueue queue = PollingsQueue.getInstance(mContext);
570             if (queue != null) {
571                 queue.setCapabilityPolling(this);
572                 queue.add(type, list, mDefaultSubId);
573             }
574         }
575     }
576 
randomCapabilityPollInterval()577     private long randomCapabilityPollInterval() {
578         double random = Math.random() * 0.2 + 0.9;
579         logger.print("The random for this time polling is: " + random);
580         random = random * mCapabilityPollInterval;
581         return (long)random;
582     }
583 
updateObsoleteItems()584     private void updateObsoleteItems() {
585         long current = System.currentTimeMillis();
586         long last = current - mCapabilityCacheExpiration;
587         long last3year = current - 3 * 365 * 24 * 3600000L;
588         StringBuilder sb = new StringBuilder();
589         sb.append("((");
590         sb.append(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP + "<='" + last + "'");
591         sb.append(" OR ");
592         sb.append(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP + "<='" + last + "'");
593         sb.append(") AND ");
594         sb.append(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP + ">='" + last3year + "'");
595         sb.append(")");
596         EABContactManager.Query baseQuery = new EABContactManager.Query()
597                 .setFilterByTime(sb.toString())
598                 .orderBy(EABContactManager.COLUMN_ID,
599                          EABContactManager.Query.ORDER_ASCENDING);
600 
601         Cursor cursor = null;
602         List<Contacts.Item> list = null;
603         try {
604             logger.debug("updateObsoleteItems.query:\n" + baseQuery);
605             cursor = mEABContactManager.query(baseQuery);
606             if (cursor == null) {
607                 logger.print("Cursor is null, there is no database found.");
608                 return;
609             }
610             int count = cursor.getCount();
611             if (count == 0) {
612                 logger.print("Cursor.getCount() is 0, there is no obsolete items found.");
613                 return;
614             }
615 
616             list = new ArrayList<Contacts.Item>();
617             list.clear();
618 
619             for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
620                 long id = cursor.getLong(cursor.getColumnIndex(Contacts.Impl._ID));
621 
622                 Contacts.Item item = new Contacts.Item(id);
623                 item.setLastUpdateTime(cursor.getLong(
624                         cursor.getColumnIndex(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP)));
625                 item.setNumber(cursor.getString(
626                         cursor.getColumnIndex(Contacts.Impl.CONTACT_NUMBER)));
627                 item.setName(cursor.getString(
628                         cursor.getColumnIndex(Contacts.Impl.CONTACT_NAME)));
629                 item.setVolteTimestamp(cursor.getLong(
630                         cursor.getColumnIndex(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP)));
631                 item.setVideoTimestamp(cursor.getLong(
632                         cursor.getColumnIndex(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP)));
633                 logger.print("updateObsoleteItems, the obsolete item:\n" + item);
634 
635                 if ((item.lastUpdateTime() > 0) && item.isValid()) {
636                     list.add(item);
637                 }
638             }
639         } catch (Exception ex) {
640             logger.warn("Exception in updateObsoleteItems: " + ex);
641         } finally {
642             if (cursor != null) {
643                 cursor.close();
644             }
645         }
646 
647         if ((list == null) || (list.size() <= 0)) {
648             return;
649         }
650 
651         for (Contacts.Item item : list) {
652             EABContactManager.Request request = new EABContactManager.Request(item.id());
653             if (item.volteTimestamp() <= last) {
654                 request.setVolteCallCapability(false);
655                 request.setVolteCallCapabilityTimeStamp(current);
656             }
657             if (item.videoTimestamp() <= last) {
658                 request.setVideoCallCapability(false);
659                 request.setVideoCallCapabilityTimeStamp(current);
660             }
661             int result = mEABContactManager.update(request);
662             if (result <= 0) {
663                 logger.print("Failed to update this request: " + request);
664             }
665         }
666     }
667 
cancelDiscoveryAlarm()668     private void cancelDiscoveryAlarm() {
669         if (mDiscoveryAlarmIntent != null) {
670             mAlarmManager.cancel(mDiscoveryAlarmIntent);
671             mDiscoveryAlarmIntent = null;
672             mNextPollingTimeStamp = 0L;
673         }
674     }
675 
clearPollingTasks()676     private void clearPollingTasks() {
677         PollingsQueue queue = PollingsQueue.getInstance(null);
678         if (queue != null) {
679             queue.clear();
680         }
681     }
682 
getTimeString(long time)683     private String getTimeString(long time) {
684         if (time <= 0) {
685             time = System.currentTimeMillis();
686         }
687 
688         String timeString = TimeMigrationUtils.formatMillisWithFixedFormat(time);
689         return String.format("%s.%s", timeString, time % 1000);
690     }
691 
692     private static final int MSG_CHECK_DISCOVERY = 1;
693     private static final int MSG_NEW_CONTACT_ADDED = 2;
694     private static final int MSG_SERVICE_STATUS_CHANGED = 3;
695     private static final int MSG_SETTINGS_CHANGED = 4;
696     private static final int MSG_PUBLISH_STATE_CHANGED = 5;
697     private static final int MSG_PROVISION_STATE_CHANGED = 6;
698     private static final int MSG_VERIFY_POLLING_RESULT = 7;
699 
enqueueDiscovery(int type)700     public void enqueueDiscovery(int type) {
701         mDiscoveryHandler.removeMessages(MSG_CHECK_DISCOVERY);
702         mDiscoveryHandler.obtainMessage(MSG_CHECK_DISCOVERY, type, -1).sendToTarget();
703     }
704 
enqueueNewContact(String number)705     public void enqueueNewContact(String number) {
706         mDiscoveryHandler.obtainMessage(MSG_NEW_CONTACT_ADDED, number).sendToTarget();
707     }
708 
enqueueServiceStatusChanged(boolean enabled)709     private void enqueueServiceStatusChanged(boolean enabled) {
710         mDiscoveryHandler.removeMessages(MSG_SERVICE_STATUS_CHANGED);
711         mDiscoveryHandler.obtainMessage(MSG_SERVICE_STATUS_CHANGED,
712                 enabled ? 1 : 0, -1).sendToTarget();
713     }
714 
enqueuePublishStateChanged(int state)715     private void enqueuePublishStateChanged(int state) {
716         mDiscoveryHandler.removeMessages(MSG_PUBLISH_STATE_CHANGED);
717         mDiscoveryHandler.obtainMessage(MSG_PUBLISH_STATE_CHANGED,
718                 state, -1).sendToTarget();
719     }
720 
enqueueSettingsChanged()721     public void enqueueSettingsChanged() {
722         mDiscoveryHandler.removeMessages(MSG_SETTINGS_CHANGED);
723         mDiscoveryHandler.obtainMessage(MSG_SETTINGS_CHANGED).sendToTarget();
724     }
725 
enqueueProvisionStateChanged()726     private void enqueueProvisionStateChanged() {
727         mDiscoveryHandler.removeMessages(MSG_PROVISION_STATE_CHANGED);
728         mDiscoveryHandler.obtainMessage(MSG_PROVISION_STATE_CHANGED).sendToTarget();
729     }
730 
enqueueVerifyPollingResult(int counts)731     public void enqueueVerifyPollingResult(int counts) {
732         mDiscoveryHandler.removeMessages(MSG_VERIFY_POLLING_RESULT);
733         mDiscoveryHandler.obtainMessage(MSG_VERIFY_POLLING_RESULT, counts, -1).sendToTarget();
734     }
735 
enqueueTryRegisterConfigCallback()736     private void enqueueTryRegisterConfigCallback() {
737         mContext.getMainThreadHandler().removeCallbacks(mRegisterCallbackRunnable);
738         mContext.getMainThreadHandler().postDelayed(mRegisterCallbackRunnable,
739                 DELAY_REGISTER_CALLBACK_MS);
740     }
741 
742     private Handler.Callback mDiscoveryCallback = new Handler.Callback() {
743         @Override
744         public boolean handleMessage(Message msg) {
745             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
746 
747             if (msg.what == MSG_CHECK_DISCOVERY) {
748                 doCapabilityDiscovery(msg.arg1);
749             } else if (msg.what == MSG_NEW_CONTACT_ADDED) {
750                 newContactAdded((String)msg.obj);
751             } else if (msg.what == MSG_SERVICE_STATUS_CHANGED) {
752                 serviceStatusChanged(msg.arg1 == 1);
753             } else if (msg.what == MSG_PUBLISH_STATE_CHANGED) {
754                 publishStateChanged(msg.arg1);
755             } else if (msg.what == MSG_SETTINGS_CHANGED) {
756                 settingsChanged();
757             } else if(msg.what == MSG_PROVISION_STATE_CHANGED) {
758                 provisionStateChanged();
759             } else if(msg.what == MSG_VERIFY_POLLING_RESULT) {
760                 verifyPollingResult(msg.arg1);
761             } else {
762             }
763 
764             return true;
765         }
766     };
767 
setDefaultSubscriberIds()768     private void setDefaultSubscriberIds() {
769         PresencePreferences pref = PresencePreferences.getInstance();
770         if (pref == null) {
771             return;
772         }
773 
774         String mdn_old = pref.getLine1Number();
775         if (TextUtils.isEmpty(mdn_old)) {
776             return;
777         }
778         String subscriberId_old = pref.getSubscriberId();
779         if (TextUtils.isEmpty(subscriberId_old)) {
780             return;
781         }
782 
783         String mdn = getLine1Number();
784         String subscriberId = getSubscriberId();
785         if (TextUtils.isEmpty(mdn) && TextUtils.isEmpty(subscriberId)) {
786             return;
787         }
788 
789         boolean mdnMatched = false;
790         if (TextUtils.isEmpty(mdn) || PhoneNumberUtils.compare(mdn_old, mdn)) {
791             mdnMatched = true;
792         }
793         boolean subscriberIdMatched = false;
794         if (TextUtils.isEmpty(subscriberId) || subscriberId.equals(subscriberId_old)) {
795             subscriberIdMatched = true;
796         }
797         if (mdnMatched && subscriberIdMatched) {
798             return;
799         }
800 
801         logger.print("Remove presence cache for Sim card changed!");
802         pref.setLine1Number("");
803         pref.setSubscriberId("");
804     }
805 
806     // Track the default subscription (the closest we can get to MSIM).
807     // call from main thread only.
handleDefaultSubscriptionChanged(int newDefaultSubId)808     public void handleDefaultSubscriptionChanged(int newDefaultSubId) {
809         logger.print("handleDefaultSubscriptionChanged: new default= "
810                 + newDefaultSubId);
811 
812         if (!SubscriptionManager.isValidSubscriptionId(newDefaultSubId)) {
813             return;
814         }
815         if (isInitializing && (mDefaultSubId == newDefaultSubId)) {
816             return;
817         } else {
818             isInitializing = true;
819         }
820         // unregister old default first
821         if (SubscriptionManager.isValidSubscriptionId(mDefaultSubId)) {
822             ProvisioningManager pm = ProvisioningManager.createForSubscriptionId(mDefaultSubId);
823             pm.unregisterProvisioningChangedCallback(mProvisioningManagerCallback);
824 
825             unregisterPublishStateChangedCallback();
826         }
827         // register new default and clear old cached values in EAB only if we are changing the
828         // default sub ID.
829         if (SubscriptionManager.isValidSubscriptionId(newDefaultSubId)
830                 && mEABContactManager != null) {
831             mEABContactManager.updateAllCapabilityToUnknown();
832         }
833         mDefaultSubId = newDefaultSubId;
834         SharedPrefUtil.saveLastUsedSubscriptionId(mContext, mDefaultSubId);
835         tryProvisioningManagerRegistration();
836         // Clear defaults and recalculate.
837         setDefaultSubscriberIds();
838         mProvisioned = -1;
839         enqueueSettingsChanged();
840         // load settings for new default.
841         enqueueProvisionStateChanged();
842         registerPublishStateChangedCallback();
843     }
844 
tryProvisioningManagerRegistration()845     public void tryProvisioningManagerRegistration() {
846         ProvisioningManager pm = ProvisioningManager.createForSubscriptionId(mDefaultSubId);
847         try {
848             pm.registerProvisioningChangedCallback(mContext.getMainExecutor(),
849                     mProvisioningManagerCallback);
850         } catch (ImsException e) {
851             if (e.getCode() == ImsException.CODE_ERROR_SERVICE_UNAVAILABLE) {
852                 enqueueTryRegisterConfigCallback();
853             }
854         }
855     }
856 
getLine1Number()857     private String getLine1Number() {
858         if (mContext == null) {
859             return null;
860         }
861 
862         TelephonyManager telephony = (TelephonyManager)
863                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
864         if (telephony == null) {
865             return null;
866         }
867 
868         int subId = PresenceSetting.getDefaultSubscriptionId();
869         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
870             // no valid subscriptions available.
871             return null;
872         }
873         telephony = telephony.createForSubscriptionId(subId);
874         String mdn = telephony.getLine1Number();
875 
876         if ((mdn == null) || (mdn.length() == 0) ||  mdn.startsWith("00000")) {
877             return null;
878         }
879 
880         logger.print("getLine1Number: " + mdn);
881         return mdn;
882     }
883 
getSubscriberId()884     private String getSubscriberId() {
885         if (mContext == null) {
886             return null;
887         }
888 
889         TelephonyManager telephony = (TelephonyManager)
890                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
891         if (telephony == null) {
892             return null;
893         }
894 
895         int subId = PresenceSetting.getDefaultSubscriptionId();
896         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
897             return null;
898         }
899         telephony.createForSubscriptionId(subId);
900         String subscriberId = telephony.getSubscriberId();
901 
902         logger.print("getSubscriberId: " + subscriberId);
903         return subscriberId;
904     }
905 }
906