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.annotation.IntDef;
32 import android.app.AlarmManager;
33 import android.app.PendingIntent;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.net.Uri;
37 import android.os.Handler;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.os.SystemClock;
41 import android.provider.Settings;
42 import android.telecom.PhoneAccount;
43 import android.telecom.TelecomManager;
44 import android.telephony.AccessNetworkConstants;
45 import android.telephony.SubscriptionManager;
46 import android.telephony.TelephonyManager;
47 import android.telephony.ims.RcsContactPresenceTuple;
48 import android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities;
49 import android.telephony.ims.RcsContactUceCapability;
50 import android.telephony.ims.RcsContactUceCapability.PresenceBuilder;
51 import android.telephony.ims.feature.MmTelFeature;
52 import android.text.TextUtils;
53 
54 import com.android.ims.ResultCode;
55 import com.android.ims.internal.ContactNumberUtils;
56 import com.android.ims.internal.Logger;
57 import com.android.service.ims.RcsSettingUtils;
58 import com.android.service.ims.Task;
59 import com.android.service.ims.TaskManager;
60 
61 import java.lang.annotation.Retention;
62 import java.lang.annotation.RetentionPolicy;
63 
64 public class PresencePublication extends PresenceBase {
65     private Logger logger = Logger.getLogger(this.getClass().getName());
66 
67     private final Object mSyncObj = new Object();
68 
69     private static final int TIMEOUT_CHECK_SUBSCRIPTION_READY_MS = 5000;
70 
71     private static final String SIP_SCHEME = "sip";
72     private static final String TEL_SCHEME = "tel";
73     private static final String DOMAIN_SEPARATOR = "@";
74 
75     boolean mMovedToIWLAN = false;
76     boolean mMovedToLTE = false;
77     boolean mVoPSEnabled = false;
78 
79     boolean mIsVolteAvailable = false;
80     boolean mIsVtAvailable = false;
81     boolean mIsVoWifiAvailable = false;
82     boolean mIsViWifiAvailable = false;
83 
84     // Queue for the pending PUBLISH request. Just need cache the last one.
85     volatile PublishRequest mPendingRequest = null;
86     volatile PublishRequest mPublishingRequest = null;
87     volatile PublishRequest mPublishedRequest = null;
88 
89     /*
90      * Message sent to mMsgHandler when there is a new request to Publish capabilities.
91      */
92     private static final int MESSAGE_RCS_PUBLISH_REQUEST = 1;
93     /*
94      * Message sent when the default subscription has changed or become active for the first time.
95      */
96     private static final int MESSAGE_DEFAULT_SUBSCRIPTION_CHANGED = 2;
97 
98     private Handler mMsgHandler = new Handler(Looper.getMainLooper()) {
99         @Override
100         public void handleMessage(Message msg) {
101             super.handleMessage(msg);
102 
103             logger.debug( "Thread=" + Thread.currentThread().getName() + " received "
104                     + msg);
105             if(msg == null){
106                 logger.error("msg=null");
107                 return;
108             }
109 
110             switch (msg.what) {
111                 case MESSAGE_RCS_PUBLISH_REQUEST: {
112                     logger.debug("handleMessage  msg=RCS_PUBLISH_REQUEST:");
113 
114                     PublishRequest publishRequest = (PublishRequest) msg.obj;
115                     synchronized (mSyncObj) {
116                         mPendingRequest = null;
117                     }
118                     doPublish(publishRequest);
119                     break;
120                 }
121                 case MESSAGE_DEFAULT_SUBSCRIPTION_CHANGED: {
122                     requestPublishIfSubscriptionReady();
123                     break;
124                 }
125                 default:
126                     logger.debug("handleMessage unknown msg=" + msg.what);
127             }
128         }
129     };
130 
131     private PresencePublisher mPresencePublisher;
132     private PresenceSubscriber mSubscriber = null;
133     static private PresencePublication sPresencePublication = null;
134 
135     private boolean mHasCachedTrigger = false;
136     private boolean mGotTriggerFromStack = false;
137     private boolean mDonotRetryUntilPowerCycle = false;
138     private boolean mSimLoaded = false;
139     private int mPreferredTtyMode = TelecomManager.TTY_MODE_OFF;
140 
141     private boolean mImsRegistered = false;
142     private boolean mVtEnabled = false;
143     private boolean mDataEnabled = false;
144     private final String[] mConfigVolteProvisionErrorOnPublishResponse;
145     private final String[] mConfigRcsProvisionErrorOnPublishResponse;
146     private int mAssociatedSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
147 
148     /** ETag expired. */
149     public static final int UCE_PRES_PUBLISH_TRIGGER_ETAG_EXPIRED = 0;
150     /** Move to LTE with VoPS disabled. */
151     public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1;
152     /** Move to LTE with VoPS enabled. */
153     public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2;
154     /** Move to eHRPD. */
155     public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_EHRPD = 3;
156     /** Move to HSPA+. */
157     public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS = 4;
158     /** Move to 3G. */
159     public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_3G = 5;
160     /** Move to 2G. */
161     public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_2G = 6;
162     /** Move to WLAN */
163     public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_WLAN = 7;
164     /** Move to IWLAN */
165     public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN = 8;
166     /** Trigger is unknown. */
167     public static final int UCE_PRES_PUBLISH_TRIGGER_UNKNOWN = 9;
168 
169     @IntDef(value = {
170             UCE_PRES_PUBLISH_TRIGGER_ETAG_EXPIRED,
171             UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED,
172             UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED,
173             UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_EHRPD,
174             UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS,
175             UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_3G,
176             UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_2G,
177             UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_WLAN,
178             UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN,
179             UCE_PRES_PUBLISH_TRIGGER_UNKNOWN
180     }, prefix="UCE_PRES_PUBLISH_TRIGGER_")
181     @Retention(RetentionPolicy.SOURCE)
182     public @interface StackPublishTriggerType {}
183 
184     public class PublishType {
185         public static final int PRES_PUBLISH_TRIGGER_DATA_CHANGED = 0;
186         // the lower layer should send trigger when enable volte
187         // the lower layer should unpublish when disable volte
188         // so only handle VT here.
189         public static final int PRES_PUBLISH_TRIGGER_VTCALL_CHANGED = 1;
190         public static final int PRES_PUBLISH_TRIGGER_CACHED_TRIGGER = 2;
191         public static final int PRES_PUBLISH_TRIGGER_TTY_ENABLE_STATUS = 3;
192         public static final int PRES_PUBLISH_TRIGGER_RETRY = 4;
193         public static final int PRES_PUBLISH_TRIGGER_FEATURE_AVAILABILITY_CHANGED = 5;
194         public static final int PRES_PUBLISH_TRIGGER_DEFAULT_SUB_CHANGED = 6;
195     };
196 
PresencePublication(PresencePublisher presencePublisher, Context context, String[] configVolteProvisionErrorOnPublishResponse, String[] configRcsProvisionErrorOnPublishResponse)197     public PresencePublication(PresencePublisher presencePublisher, Context context,
198             String[] configVolteProvisionErrorOnPublishResponse,
199             String[] configRcsProvisionErrorOnPublishResponse) {
200         super(context);
201         logger.debug("PresencePublication constrcuct");
202         synchronized(mSyncObj) {
203             this.mPresencePublisher = presencePublisher;
204         }
205         mConfigVolteProvisionErrorOnPublishResponse = configVolteProvisionErrorOnPublishResponse;
206         mConfigRcsProvisionErrorOnPublishResponse = configRcsProvisionErrorOnPublishResponse;
207 
208         mVtEnabled = RcsSettingUtils.isVtEnabledByUser(mAssociatedSubscription);
209 
210         mDataEnabled = Settings.Global.getInt(mContext.getContentResolver(),
211                     Settings.Global.MOBILE_DATA, 1) == 1;
212         logger.debug("The current mobile data is: " + (mDataEnabled ? "enabled" : "disabled"));
213 
214         TelecomManager tm = mContext.getSystemService(TelecomManager.class);
215         mPreferredTtyMode = tm.getCurrentTtyMode();
216         logger.debug("The current TTY mode is: " + mPreferredTtyMode);
217 
218         sPresencePublication = this;
219     }
220 
updatePresencePublisher(PresencePublisher presencePublisher)221     public void updatePresencePublisher(PresencePublisher presencePublisher) {
222         synchronized(mSyncObj) {
223             logger.debug("Update PresencePublisher");
224             this.mPresencePublisher = presencePublisher;
225         }
226     }
227 
removePresencePublisher()228     public void removePresencePublisher() {
229         synchronized(mSyncObj) {
230             logger.debug("Remove PresencePublisher");
231             this.mPresencePublisher = null;
232         }
233     }
234 
requestPublishIfSubscriptionReady()235     private void requestPublishIfSubscriptionReady() {
236         if (!SubscriptionManager.isValidSubscriptionId(mAssociatedSubscription)) {
237             // pulled out the SIM, set it as the same as power on status:
238             logger.print("subscription changed to invalid, setting to not published");
239 
240             // only reset when the SIM gets absent.
241             reset();
242             mSimLoaded = false;
243             setPublishState(PUBLISH_STATE_NOT_PUBLISHED);
244             return;
245         }
246         if (isSimLoaded()) {
247             logger.print("subscription ready, requesting publish");
248             mSimLoaded = true;
249             // treat hot SIM hot swap as power on.
250             mDonotRetryUntilPowerCycle = false;
251             requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_DEFAULT_SUB_CHANGED);
252         } else {
253             mMsgHandler.removeMessages(MESSAGE_DEFAULT_SUBSCRIPTION_CHANGED);
254             mMsgHandler.sendMessageDelayed(
255                     mMsgHandler.obtainMessage(MESSAGE_DEFAULT_SUBSCRIPTION_CHANGED),
256                     TIMEOUT_CHECK_SUBSCRIPTION_READY_MS);
257         }
258     }
259 
isSimLoaded()260     private boolean isSimLoaded() {
261         TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
262                 Context.TELEPHONY_SERVICE);
263         if (teleMgr == null) return false;
264         teleMgr = teleMgr.createForSubscriptionId(mAssociatedSubscription);
265         String[] myImpu = teleMgr.getIsimImpu();
266         String myDomain = teleMgr.getIsimDomain();
267         String line1Number = teleMgr.getLine1Number();
268         return !TextUtils.isEmpty(line1Number) || (!TextUtils.isEmpty(myDomain) && myImpu != null
269                 && myImpu.length != 0);
270     }
271 
isIPVoiceSupported(boolean volteAvailable, boolean voWifiAvailable)272     private boolean isIPVoiceSupported(boolean volteAvailable, boolean voWifiAvailable) {
273         // volte and vowifi can be enabled separately
274         if(!RcsSettingUtils.isVoLteSupported(mAssociatedSubscription) &&
275                 !RcsSettingUtils.isVoWiFiSupported(mAssociatedSubscription)) {
276             logger.print("Disabled by platform, voiceSupported=false");
277             return false;
278         }
279 
280         if(!RcsSettingUtils.isVoLteProvisioned(mAssociatedSubscription) &&
281                 !RcsSettingUtils.isVowifiProvisioned(mAssociatedSubscription)) {
282             logger.print("Wasn't provisioned, voiceSupported=false");
283             return false;
284         }
285 
286         if(!RcsSettingUtils.isAdvancedCallingEnabledByUser(mAssociatedSubscription) &&
287                 !RcsSettingUtils.isWfcEnabledByUser(mAssociatedSubscription)){
288             logger.print("User didn't enable volte or wfc, voiceSupported=false");
289             return false;
290         }
291 
292         // for IWLAN. All WFC settings and provision should be fine if voWifiAvailable is true.
293         if(isOnIWLAN()) {
294             boolean voiceSupported=volteAvailable || voWifiAvailable;
295             logger.print("on IWLAN, voiceSupported=" + voiceSupported);
296             return voiceSupported;
297         }
298 
299         // for none-IWLAN
300         if(!isOnLTE()) {
301             logger.print("isOnLTE=false, voiceSupported=false");
302             return false;
303         }
304 
305         if(!mVoPSEnabled) {
306             logger.print("mVoPSEnabled=false, voiceSupported=false");
307             return false;
308         }
309 
310         logger.print("voiceSupported=true");
311         return true;
312     }
313 
isIPVideoSupported(boolean vtAvailable, boolean viWifiAvailable)314     private boolean isIPVideoSupported(boolean vtAvailable, boolean viWifiAvailable) {
315         // if volte or vt was disabled then the viwifi will be disabled as well.
316         if(!RcsSettingUtils.isVoLteSupported(mAssociatedSubscription) ||
317                 !RcsSettingUtils.isVtSupported(mAssociatedSubscription)) {
318             logger.print("Disabled by platform, videoSupported=false");
319             return false;
320         }
321 
322         if(!RcsSettingUtils.isVoLteProvisioned(mAssociatedSubscription) ||
323                 !RcsSettingUtils.isLvcProvisioned(mAssociatedSubscription)) {
324             logger.print("Not provisioned. videoSupported=false");
325             return false;
326         }
327 
328         if(!RcsSettingUtils.isAdvancedCallingEnabledByUser(mAssociatedSubscription) || !mVtEnabled){
329             logger.print("User disabled volte or vt, videoSupported=false");
330             return false;
331         }
332 
333         if(isTtyOn()){
334             logger.print("isTtyOn=true, videoSupported=false");
335             return false;
336         }
337 
338         // for IWLAN, all WFC settings and provision should be fine if viWifiAvailable is true.
339         if(isOnIWLAN()) {
340             boolean videoSupported = vtAvailable || viWifiAvailable;
341             logger.print("on IWLAN, videoSupported=" + videoSupported);
342             return videoSupported;
343         }
344 
345         if(!isDataEnabled()) {
346             logger.print("isDataEnabled()=false, videoSupported=false");
347             return false;
348         }
349 
350         if(!isOnLTE()) {
351             logger.print("isOnLTE=false, videoSupported=false");
352             return false;
353         }
354 
355         if(!mVoPSEnabled) {
356             logger.print("mVoPSEnabled=false, videoSupported=false");
357             return false;
358         }
359 
360         return true;
361     }
362 
onTtyPreferredModeChanged(int newTtyPreferredMode)363     public void onTtyPreferredModeChanged(int newTtyPreferredMode) {
364         logger.debug("Tty mode changed from " + mPreferredTtyMode
365                 + " to " + newTtyPreferredMode);
366 
367         boolean mIsTtyEnabled = isTtyEnabled(mPreferredTtyMode);
368         boolean isTtyEnabled = isTtyEnabled(newTtyPreferredMode);
369         mPreferredTtyMode = newTtyPreferredMode;
370         if (mIsTtyEnabled != isTtyEnabled) {
371             logger.print("ttyEnabled status changed from " + mIsTtyEnabled
372                     + " to " + isTtyEnabled);
373             requestLocalPublish(PresencePublication.PublishType.
374                     PRES_PUBLISH_TRIGGER_TTY_ENABLE_STATUS );
375         }
376     }
377 
onAirplaneModeChanged(boolean isAirplaneModeEnabled)378     public void onAirplaneModeChanged(boolean isAirplaneModeEnabled) {
379         if(isAirplaneModeEnabled){
380             logger.print("Airplane mode, set to PUBLISH_STATE_NOT_PUBLISHED");
381             reset();
382             setPublishState(PUBLISH_STATE_NOT_PUBLISHED);
383         }
384     }
385 
isTtyOn()386     public boolean isTtyOn() {
387         logger.debug("isTtyOn settingsTtyMode=" + mPreferredTtyMode);
388         return isTtyEnabled(mPreferredTtyMode);
389     }
390 
onImsConnected()391     public void onImsConnected() {
392         mImsRegistered = true;
393     }
394 
onImsDisconnected()395     public void onImsDisconnected() {
396         logger.debug("reset PUBLISH status for IMS had been disconnected");
397         mImsRegistered = false;
398         reset();
399     }
400 
reset()401     private void reset() {
402         mIsVolteAvailable = false;
403         mIsVtAvailable = false;
404         mIsVoWifiAvailable = false;
405         mIsViWifiAvailable = false;
406 
407         synchronized(mSyncObj) {
408             mPendingRequest = null; //ignore the previous request
409             mPublishingRequest = null;
410             mPublishedRequest = null;
411         }
412     }
413 
handleAssociatedSubscriptionChanged(int newSubId)414     public void handleAssociatedSubscriptionChanged(int newSubId) {
415         if (mAssociatedSubscription == newSubId) {
416             return;
417         }
418         reset();
419         mAssociatedSubscription = newSubId;
420         mMsgHandler.removeMessages(MESSAGE_DEFAULT_SUBSCRIPTION_CHANGED);
421         mMsgHandler.sendMessage(mMsgHandler.obtainMessage(MESSAGE_DEFAULT_SUBSCRIPTION_CHANGED));
422     }
423 
handleProvisioningChanged()424     public void handleProvisioningChanged() {
425         if(RcsSettingUtils.isEabProvisioned(mContext, mAssociatedSubscription)) {
426             logger.debug("provisioned, set mDonotRetryUntilPowerCycle to false");
427             mDonotRetryUntilPowerCycle = false;
428             if(mHasCachedTrigger) {
429                 requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_CACHED_TRIGGER);
430             }
431         }
432     }
433 
getPresencePublication()434     static public PresencePublication getPresencePublication() {
435         return sPresencePublication;
436     }
437 
setSubscriber(PresenceSubscriber subscriber)438     public void setSubscriber(PresenceSubscriber subscriber) {
439         mSubscriber = subscriber;
440     }
441 
isDataEnabled()442     public boolean isDataEnabled() {
443         return  Settings.Global.getInt(mContext.getContentResolver(),
444                 Settings.Global.MOBILE_DATA, 1) == 1;
445     }
446 
onMobileDataChanged(boolean value)447     public void onMobileDataChanged(boolean value){
448         logger.print("onMobileDataChanged, mDataEnabled=" + mDataEnabled + " value=" + value);
449         if(mDataEnabled != value) {
450             mDataEnabled = value;
451 
452             requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_DATA_CHANGED);
453         }
454     }
455 
onVtEnabled(boolean enabled)456     public void onVtEnabled(boolean enabled) {
457         logger.debug("onVtEnabled mVtEnabled=" + mVtEnabled + " enabled=" + enabled);
458 
459         if(mVtEnabled != enabled) {
460             mVtEnabled = enabled;
461             requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_VTCALL_CHANGED);
462         }
463     }
464 
465     @Override
onCommandStatusUpdated(int taskId, int requestId, int resultCode)466     public void onCommandStatusUpdated(int taskId, int requestId, int resultCode) {
467         logger.info("onCommandStatusUpdated: resultCode= " + resultCode);
468         super.onCommandStatusUpdated(taskId, requestId, resultCode);
469     }
470 
471     /**
472      * @return return true if it had been PUBLISHED.
473      */
isPublishedOrPublishing()474     private boolean isPublishedOrPublishing() {
475         long publishThreshold = RcsSettingUtils.getPublishThrottle(mAssociatedSubscription);
476 
477         boolean publishing = false;
478         publishing = (mPublishingRequest != null) &&
479                 (System.currentTimeMillis() - mPublishingRequest.getTimestamp()
480                 <= publishThreshold);
481 
482         return (getPublishState() == PUBLISH_STATE_200_OK || publishing);
483     }
484 
485     /**
486      * @return The result of the last Publish request.
487      */
getPublishState()488     public @PresenceBase.PresencePublishState int getPublishState() {
489         PresencePublisher presencePublisher = null;
490         synchronized(mSyncObj) {
491             presencePublisher = mPresencePublisher;
492         }
493 
494         if (presencePublisher != null) {
495             return presencePublisher.getPublisherState();
496         }
497         return PUBLISH_STATE_NOT_PUBLISHED;
498     }
499 
500     /**
501      * @param publishState The result of the last publish request.
502      */
setPublishState(int publishState)503     public void setPublishState(int publishState) {
504         PresencePublisher presencePublisher = null;
505         synchronized(mSyncObj) {
506             presencePublisher = mPresencePublisher;
507         }
508 
509         if (presencePublisher != null) {
510             presencePublisher.updatePublisherState(publishState);
511         }
512     }
513 
514     // Trigger a local publish based off of state changes in the framework.
requestLocalPublish(int trigger)515     private void requestLocalPublish(int trigger) {
516 
517         // if the value is true then it will call stack to send the PUBLISH
518         // though the previous publish had the same capability
519         // for example: for retry PUBLISH.
520         boolean bForceToNetwork = true;
521         switch(trigger)
522         {
523             case PublishType.PRES_PUBLISH_TRIGGER_DATA_CHANGED:
524             {
525                 logger.print("PRES_PUBLISH_TRIGGER_DATA_CHANGED");
526                 bForceToNetwork = false;
527                 break;
528             }
529             case PublishType.PRES_PUBLISH_TRIGGER_VTCALL_CHANGED:
530             {
531                 logger.print("PRES_PUBLISH_TRIGGER_VTCALL_CHANGED");
532                 // Didn't get featureCapabilityChanged sometimes.
533                 bForceToNetwork = true;
534                 break;
535             }
536             case PublishType.PRES_PUBLISH_TRIGGER_CACHED_TRIGGER:
537             {
538                 logger.print("PRES_PUBLISH_TRIGGER_CACHED_TRIGGER");
539                 break;
540             }
541             case PublishType.PRES_PUBLISH_TRIGGER_TTY_ENABLE_STATUS:
542             {
543                 logger.print("PRES_PUBLISH_TRIGGER_TTY_ENABLE_STATUS");
544                 bForceToNetwork = true;
545 
546                 break;
547             }
548             case PublishType.PRES_PUBLISH_TRIGGER_FEATURE_AVAILABILITY_CHANGED:
549             {
550                 bForceToNetwork = false;
551                 logger.print("PRES_PUBLISH_TRIGGER_FEATURE_AVAILABILITY_CHANGED");
552                 break;
553             }
554             case PublishType.PRES_PUBLISH_TRIGGER_RETRY:
555             {
556                 logger.print("PRES_PUBLISH_TRIGGER_RETRY");
557                 break;
558             }
559             case PublishType.PRES_PUBLISH_TRIGGER_DEFAULT_SUB_CHANGED:
560             {
561                 logger.print("PRES_PUBLISH_TRIGGER_DEFAULT_SUB_CHANGED");
562                 bForceToNetwork = true;
563                 break;
564             }
565             default:
566             {
567                 logger.print("Unknown publish trigger from AP");
568             }
569         }
570 
571         if(mGotTriggerFromStack == false){
572             // Waiting for RCS stack get ready.
573             logger.print("Didn't get trigger from stack yet, discard framework trigger.");
574             return;
575         }
576 
577         if (mDonotRetryUntilPowerCycle) {
578             logger.print("Don't publish until next power cycle");
579             return;
580         }
581 
582         if(!mSimLoaded){
583             //need to read some information from SIM to publish
584             logger.print("invokePublish cache the trigger since the SIM is not ready");
585             mHasCachedTrigger = true;
586             return;
587         }
588 
589         //the provision status didn't be read from modem yet
590         if(!RcsSettingUtils.isEabProvisioned(mContext, mAssociatedSubscription)) {
591             logger.print("invokePublish cache the trigger, not provision yet");
592             mHasCachedTrigger = true;
593             return;
594         }
595 
596         PublishRequest publishRequest = new PublishRequest(
597                 bForceToNetwork, System.currentTimeMillis());
598 
599         requestPublication(publishRequest);
600 
601         return;
602     }
603 
onStackPublishRequested(@tackPublishTriggerType int publishTriggerType)604     public void onStackPublishRequested(@StackPublishTriggerType int publishTriggerType) {
605         mGotTriggerFromStack = true;
606 
607         switch (publishTriggerType)
608         {
609             case UCE_PRES_PUBLISH_TRIGGER_ETAG_EXPIRED:
610             {
611                 logger.print("PUBLISH_TRIGGER_ETAG_EXPIRED");
612                 break;
613             }
614             case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED:
615             {
616                 logger.print("PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED");
617                 mMovedToLTE = true;
618                 mVoPSEnabled = false;
619                 mMovedToIWLAN = false;
620 
621                 // onImsConnected could came later than this trigger.
622                 mImsRegistered = true;
623                 break;
624             }
625             case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED:
626             {
627                 logger.print("PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED");
628                 mMovedToLTE = true;
629                 mVoPSEnabled = true;
630                 mMovedToIWLAN = false;
631 
632                 // onImsConnected could came later than this trigger.
633                 mImsRegistered = true;
634                 break;
635             }
636             case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN:
637             {
638                 logger.print("QRCS_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN");
639                 mMovedToLTE = false;
640                 mVoPSEnabled = false;
641                 mMovedToIWLAN = true;
642 
643                 // onImsConnected could came later than this trigger.
644                 mImsRegistered = true;
645                 break;
646             }
647             case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_EHRPD:
648             {
649                 logger.print("PUBLISH_TRIGGER_MOVE_TO_EHRPD");
650                 mMovedToLTE = false;
651                 mVoPSEnabled = false;
652                 mMovedToIWLAN = false;
653 
654                 // onImsConnected could came later than this trigger.
655                 mImsRegistered = true;
656                 break;
657             }
658             case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS:
659             {
660                 logger.print("PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS");
661                 mMovedToLTE = false;
662                 mVoPSEnabled = false;
663                 mMovedToIWLAN = false;
664                 break;
665             }
666             case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_2G:
667             {
668                 logger.print("PUBLISH_TRIGGER_MOVE_TO_2G");
669                 mMovedToLTE = false;
670                 mVoPSEnabled = false;
671                 mMovedToIWLAN = false;
672                 break;
673             }
674             case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_3G:
675             {
676                 logger.print("PUBLISH_TRIGGER_MOVE_TO_3G");
677                 mMovedToLTE = false;
678                 mVoPSEnabled = false;
679                 mMovedToIWLAN = false;
680                 break;
681             }
682             default:
683                 logger.print("Unknow Publish Trigger Type");
684         }
685 
686         if (mDonotRetryUntilPowerCycle) {
687             logger.print("Don't publish until next power cycle");
688             return;
689         }
690 
691         if(!mSimLoaded){
692             //need to read some information from SIM to publish
693             logger.print("invokePublish cache the trigger since the SIM is not ready");
694             mHasCachedTrigger = true;
695             return;
696         }
697 
698         //the provision status didn't be read from modem yet
699         if(!RcsSettingUtils.isEabProvisioned(mContext, mAssociatedSubscription)) {
700             logger.print("invokePublish cache the trigger, not provision yet");
701             mHasCachedTrigger = true;
702             return;
703         }
704 
705         // Always send the PUBLISH when we got the trigger from stack.
706         // This can avoid any issue when switch the phone between IWLAN and LTE.
707         PublishRequest publishRequest = new PublishRequest(
708                 true /*forceToNetwork*/, System.currentTimeMillis());
709 
710         requestPublication(publishRequest);
711     }
712 
713     /**
714      * Trigger a publish when the stack becomes available and we have a cached trigger waiting.
715      */
onStackAvailable()716     public void onStackAvailable() {
717         if (mHasCachedTrigger) {
718             requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_CACHED_TRIGGER);
719         }
720     }
721 
722     public class PublishRequest {
723         private boolean mForceToNetwork;
724         private long mCurrentTime;
725         private boolean mVolteCapable = false;
726         private boolean mVtCapable = false;
727 
PublishRequest(boolean bForceToNetwork, long currentTime)728         PublishRequest(boolean bForceToNetwork, long currentTime) {
729             refreshPublishContent();
730             mForceToNetwork = bForceToNetwork;
731             mCurrentTime = currentTime;
732         }
733 
refreshPublishContent()734         public void refreshPublishContent() {
735             setVolteCapable(isIPVoiceSupported(mIsVolteAvailable, mIsVoWifiAvailable));
736             setVtCapable(isIPVideoSupported(mIsVtAvailable, mIsViWifiAvailable));
737         }
738 
getForceToNetwork()739         public boolean getForceToNetwork() {
740             return mForceToNetwork;
741         }
742 
setForceToNetwork(boolean bForceToNetwork)743         public void setForceToNetwork(boolean bForceToNetwork) {
744             mForceToNetwork = bForceToNetwork;
745         }
746 
getTimestamp()747         public long getTimestamp() {
748             return mCurrentTime;
749         }
750 
setTimestamp(long currentTime)751         public void setTimestamp(long currentTime) {
752             mCurrentTime = currentTime;
753         }
754 
setVolteCapable(boolean capable)755         public void setVolteCapable(boolean capable) {
756             mVolteCapable = capable;
757         }
758 
setVtCapable(boolean capable)759         public void setVtCapable(boolean capable) {
760             mVtCapable = capable;
761         }
762 
getVolteCapable()763         public boolean getVolteCapable() {
764             return mVolteCapable;
765         }
766 
getVtCapable()767         public boolean getVtCapable() {
768             return mVtCapable;
769         }
770 
hasSamePublishContent(PublishRequest request)771         public boolean hasSamePublishContent(PublishRequest request) {
772             if(request == null) {
773                 logger.error("request == null");
774                 return false;
775             }
776 
777             return (mVolteCapable == request.getVolteCapable() &&
778                     mVtCapable == request.getVtCapable());
779         }
780 
toString()781         public String toString(){
782             return "mForceToNetwork=" + mForceToNetwork +
783                     " mCurrentTime=" + mCurrentTime +
784                     " mVolteCapable=" + mVolteCapable +
785                     " mVtCapable=" + mVtCapable;
786         }
787     }
788 
requestPublication(PublishRequest publishRequest)789     private void requestPublication(PublishRequest publishRequest) {
790         // this is used to avoid the temp false feature change when switched between network.
791         if(publishRequest == null) {
792             logger.error("Invalid parameter publishRequest == null");
793             return;
794         }
795 
796         long requestThrottle = 2000;
797         long currentTime = System.currentTimeMillis();
798         synchronized(mSyncObj){
799             // There is a PUBLISH will be done soon. Discard it
800             if((mPendingRequest != null) && currentTime - mPendingRequest.getTimestamp()
801                     <= requestThrottle) {
802                 logger.print("A publish is pending, update the pending request and discard this one");
803                 if(publishRequest.getForceToNetwork() && !mPendingRequest.getForceToNetwork()) {
804                     mPendingRequest.setForceToNetwork(true);
805                 }
806                 mPendingRequest.setTimestamp(publishRequest.getTimestamp());
807                 return;
808             }
809 
810             mPendingRequest = publishRequest;
811         }
812 
813         Message publishMessage = mMsgHandler.obtainMessage(
814                 MESSAGE_RCS_PUBLISH_REQUEST, mPendingRequest);
815         mMsgHandler.sendMessageDelayed(publishMessage, requestThrottle);
816     }
817 
doPublish(PublishRequest publishRequest)818     private void doPublish(PublishRequest publishRequest) {
819 
820         if(publishRequest == null) {
821             logger.error("publishRequest == null");
822             return;
823         }
824 
825         PresencePublisher presencePublisher = null;
826         synchronized(mSyncObj) {
827             presencePublisher = mPresencePublisher;
828         }
829 
830         if (presencePublisher == null) {
831             logger.error("mPresencePublisher == null");
832             return;
833         }
834 
835         if(!mImsRegistered) {
836             logger.error("IMS wasn't registered");
837             return;
838         }
839 
840         // Since we are doing a publish, don't need the retry any more.
841         if(mPendingRetry) {
842             mPendingRetry = false;
843             mAlarmManager.cancel(mRetryAlarmIntent);
844         }
845 
846         // always publish the latest status.
847         synchronized(mSyncObj) {
848             publishRequest.refreshPublishContent();
849         }
850 
851         logger.print("publishRequest=" + publishRequest);
852         if(!publishRequest.getForceToNetwork() && isPublishedOrPublishing() &&
853                 (publishRequest.hasSamePublishContent(mPublishingRequest) ||
854                         (mPublishingRequest == null) &&
855                         publishRequest.hasSamePublishContent(mPublishedRequest)) &&
856                 (getPublishState() != PUBLISH_STATE_NOT_PUBLISHED)) {
857             logger.print("Don't need publish since the capability didn't change publishRequest "
858                     + publishRequest + " getPublishState()=" + getPublishState());
859             return;
860         }
861 
862         // Don't publish too frequently. Checking the throttle timer.
863         if(isPublishedOrPublishing()) {
864             if(mPendingRetry) {
865                 logger.print("Pending a retry");
866                 return;
867             }
868 
869             long publishThreshold = RcsSettingUtils.getPublishThrottle(mAssociatedSubscription);
870             long passed = publishThreshold;
871             if(mPublishingRequest != null) {
872                 passed = System.currentTimeMillis() - mPublishingRequest.getTimestamp();
873             } else if(mPublishedRequest != null) {
874                 passed = System.currentTimeMillis() - mPublishedRequest.getTimestamp();
875             }
876             passed = passed>=0?passed:publishThreshold;
877 
878             long left = publishThreshold - passed;
879             left = left>120000?120000:left; // Don't throttle more then 2 mintues.
880             if(left > 0) {
881                 // A publish is ongoing or published in 1 minute.
882                 // Since the new publish has new status. So schedule
883                 // the publish after the throttle timer.
884                 scheduleRetryPublish(left);
885                 return;
886             }
887         }
888 
889         // we need send PUBLISH once even the volte is off when power on the phone.
890         // That will tell other phone that it has no volte/vt capability.
891         if(!RcsSettingUtils.isAdvancedCallingEnabledByUser(mAssociatedSubscription) &&
892                 getPublishState() != PUBLISH_STATE_NOT_PUBLISHED) {
893             // volte was not enabled.
894             // or it is turnning off volte. lower layer should unpublish
895             reset();
896             return;
897         }
898 
899         TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
900                 Context.TELEPHONY_SERVICE);
901         teleMgr = teleMgr.createForSubscriptionId(mAssociatedSubscription);
902         if (teleMgr == null) {
903             logger.error("TelephonyManager not available.");
904             return;
905         }
906         Uri myUri = getUriForPublication();
907         if (myUri == null) {
908             logger.error("doPublish, myUri is null");
909             return;
910         }
911 
912         boolean isVolteCapble = publishRequest.getVolteCapable();
913         boolean isVtCapable = publishRequest.getVtCapable();
914         RcsContactUceCapability presenceInfo =
915                 getRcsContactUceCapability(myUri, isVolteCapble, isVtCapable);
916 
917         synchronized(mSyncObj) {
918             mPublishingRequest = publishRequest;
919             mPublishingRequest.setTimestamp(System.currentTimeMillis());
920         }
921 
922         String myNumber = getNumberFromUri(myUri);
923         int taskId = TaskManager.getDefault().addPublishTask(myNumber);
924         logger.print("doPublish, uri=" + myUri + ", myNumber=" + myNumber + ", taskId=" + taskId);
925         int ret = presencePublisher.requestPublication(presenceInfo, myUri.toString(), taskId);
926         if (ret != ResultCode.SUCCESS) {
927             logger.print("doPublish, task=" + taskId + " failed with code=" + ret);
928             TaskManager.getDefault().removeTask(taskId);
929         }
930         // cache the latest publication request if temporarily not available.
931         mHasCachedTrigger = (ret == ResultCode.ERROR_SERVICE_NOT_AVAILABLE);
932     }
933 
getRcsContactUceCapability(Uri contact, boolean isVolteCapable, boolean isVtCapable)934     private RcsContactUceCapability getRcsContactUceCapability(Uri contact, boolean isVolteCapable,
935             boolean isVtCapable) {
936 
937         ServiceCapabilities.Builder servCapsBuilder = new ServiceCapabilities.Builder(
938             isVolteCapable, isVtCapable);
939         servCapsBuilder.addSupportedDuplexMode(ServiceCapabilities.DUPLEX_MODE_FULL);
940 
941         RcsContactPresenceTuple.Builder tupleBuilder = new RcsContactPresenceTuple.Builder(
942                 RcsContactPresenceTuple.TUPLE_BASIC_STATUS_OPEN,
943                 RcsContactPresenceTuple.SERVICE_ID_MMTEL, "1.0");
944         tupleBuilder.setContactUri(contact)
945                 .setServiceCapabilities(servCapsBuilder.build());
946 
947         PresenceBuilder presenceBuilder = new PresenceBuilder(contact,
948                 RcsContactUceCapability.SOURCE_TYPE_CACHED,
949                 RcsContactUceCapability.REQUEST_RESULT_FOUND);
950         presenceBuilder.addCapabilityTuple(tupleBuilder.build());
951 
952         return presenceBuilder.build();
953     }
954 
getNumberFromUri(Uri uri)955     private String getNumberFromUri(Uri uri) {
956         if (uri == null) return null;
957         String number = uri.getSchemeSpecificPart();
958         String[] numberParts = number.split("[@;:]");
959 
960         if (numberParts.length == 0) {
961             logger.error("getNumberFromUri: invalid uri=" + uri);
962             return null;
963         }
964         return numberParts[0];
965     }
966 
getUriForPublication()967     private Uri getUriForPublication() {
968         TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
969                 Context.TELEPHONY_SERVICE);
970         if (teleMgr == null) {
971             logger.error("getUriForPublication, teleMgr = null");
972             return null;
973         }
974         teleMgr = teleMgr.createForSubscriptionId(mAssociatedSubscription);
975 
976         Uri myNumUri = null;
977         String myDomain = teleMgr.getIsimDomain();
978         logger.debug("myDomain=" + myDomain);
979         if (!TextUtils.isEmpty(myDomain)) {
980             String[] impu = teleMgr.getIsimImpu();
981             if (impu != null) {
982                 for (int i = 0; i < impu.length; i++) {
983                     logger.debug("impu[" + i + "]=" + impu[i]);
984                     if (!TextUtils.isEmpty(impu[i])) {
985                         Uri impuUri = Uri.parse(impu[i]);
986                         if (SIP_SCHEME.equals(impuUri.getScheme()) &&
987                                 impuUri.getSchemeSpecificPart().endsWith(myDomain)) {
988                             myNumUri = impuUri;
989                             logger.debug("impu[" + i + "] -> uri:" + myNumUri);
990                         }
991                         break;
992                     }
993                 }
994             }
995         }
996 
997         // Try to parse URI, if it works, we are good!
998         String myNumber = myNumUri == null ? null : myNumUri.getSchemeSpecificPart();
999         if (!TextUtils.isEmpty(myNumber)) {
1000             return myNumUri;
1001         }
1002 
1003         // Fall back to trying to use the line 1 number to construct URI
1004         myNumber = ContactNumberUtils.getDefault().format(teleMgr.getLine1Number());
1005         if (myNumber == null) return null;
1006         if (!TextUtils.isEmpty(myDomain)) {
1007             return Uri.fromParts(SIP_SCHEME, myNumber + DOMAIN_SEPARATOR + myDomain, null);
1008         } else {
1009             return Uri.fromParts(TEL_SCHEME, myNumber, null);
1010         }
1011     }
1012 
1013     private PendingIntent mRetryAlarmIntent = null;
1014     public static final String ACTION_RETRY_PUBLISH_ALARM =
1015             "com.android.service.ims.presence.retry.publish";
1016     private AlarmManager mAlarmManager = null;
1017     boolean mCancelRetry = true;
1018     boolean mPendingRetry = false;
1019 
scheduleRetryPublish(long timeSpan)1020     private void scheduleRetryPublish(long timeSpan) {
1021         logger.print("timeSpan=" + timeSpan +
1022                 " mPendingRetry=" + mPendingRetry +
1023                 " mCancelRetry=" + mCancelRetry);
1024 
1025         // avoid duplicated retry.
1026         if(mPendingRetry) {
1027             logger.debug("There was a retry already");
1028             return;
1029         }
1030         mPendingRetry = true;
1031         mCancelRetry = false;
1032 
1033         Intent intent = new Intent(ACTION_RETRY_PUBLISH_ALARM);
1034         intent.setPackage(mContext.getPackageName());
1035         mRetryAlarmIntent = PendingIntent.getBroadcast(mContext, 0, intent,
1036                 PendingIntent.FLAG_UPDATE_CURRENT|PendingIntent.FLAG_IMMUTABLE);
1037 
1038         if(mAlarmManager == null) {
1039             mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
1040         }
1041 
1042         mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1043                 SystemClock.elapsedRealtime() + timeSpan, mRetryAlarmIntent);
1044     }
1045 
retryPublish()1046     public void retryPublish() {
1047         logger.print("mCancelRetry=" + mCancelRetry);
1048         mPendingRetry = false;
1049 
1050         // Need some time to cancel it (1 minute for longest)
1051         // Don't do it if it was canceled already.
1052         if(mCancelRetry) {
1053             return;
1054         }
1055 
1056         requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_RETRY);
1057     }
1058 
onSipResponse(int requestId, int responseCode, String reasonPhrase)1059     public void onSipResponse(int requestId, int responseCode, String reasonPhrase) {
1060         logger.print( "Publish response code = " + responseCode
1061                 +"Publish response reason phrase = " + reasonPhrase);
1062 
1063         synchronized(mSyncObj) {
1064             mPublishedRequest = mPublishingRequest;
1065             mPublishingRequest = null;
1066         }
1067 
1068         if (isInConfigList(responseCode, reasonPhrase,
1069                 mConfigVolteProvisionErrorOnPublishResponse)) {
1070             logger.print("volte provision error. sipCode=" + responseCode + " phrase=" +
1071                     reasonPhrase);
1072             setPublishState(PUBLISH_STATE_VOLTE_PROVISION_ERROR);
1073             mDonotRetryUntilPowerCycle = true;
1074 
1075             notifyDm();
1076 
1077             return;
1078         }
1079 
1080         if (isInConfigList(responseCode, reasonPhrase, mConfigRcsProvisionErrorOnPublishResponse)) {
1081             logger.print("rcs provision error.sipCode=" + responseCode + " phrase=" + reasonPhrase);
1082             setPublishState(PUBLISH_STATE_RCS_PROVISION_ERROR);
1083             mDonotRetryUntilPowerCycle = true;
1084 
1085             return;
1086         }
1087 
1088         switch (responseCode) {
1089             case 999:
1090                 logger.debug("Publish ignored - No capability change");
1091                 break;
1092             case 200:
1093                 setPublishState(PUBLISH_STATE_200_OK);
1094                 if(mSubscriber != null) {
1095                     mSubscriber.retryToGetAvailability();
1096                 }
1097                 break;
1098 
1099             case 408:
1100                 setPublishState(PUBLISH_STATE_REQUEST_TIMEOUT);
1101                 break;
1102 
1103             default: // Generic Failure
1104                 if ((responseCode < 100) || (responseCode > 699)) {
1105                     logger.debug("Ignore internal response code, sipCode=" + responseCode);
1106                     // internal error
1107                     //  0- PERMANENT ERROR: UI should not retry immediately
1108                     //  888- TEMP ERROR:  UI Can retry immediately
1109                     //  999- NO CHANGE: No Publish needs to be sent
1110                     if(responseCode == 888) {
1111                         // retry per 2 minutes
1112                         scheduleRetryPublish(120000);
1113                     } else {
1114                         logger.debug("Ignore internal response code, sipCode=" + responseCode);
1115                     }
1116                 } else {
1117                     logger.debug( "Generic Failure");
1118                     setPublishState(PUBLISH_STATE_OTHER_ERROR);
1119 
1120                     if ((responseCode>=400) && (responseCode <= 699)) {
1121                         // 4xx/5xx/6xx, No retry, no impact on subsequent publish
1122                         logger.debug( "No Retry in OEM");
1123                     }
1124                 }
1125                 break;
1126         }
1127 
1128         // Suppose the request ID had been set when IQPresListener_CMDStatus
1129         Task task = TaskManager.getDefault().getTaskByRequestId(requestId);
1130         if(task != null){
1131             task.mSipResponseCode = responseCode;
1132             task.mSipReasonPhrase = reasonPhrase;
1133         }
1134 
1135         handleCallback(task, getPublishState(), false);
1136     }
1137 
isTtyEnabled(int mode)1138     private static boolean isTtyEnabled(int mode) {
1139         return TelecomManager.TTY_MODE_OFF != mode;
1140     }
1141 
onFeatureCapabilityChanged(int networkType, MmTelFeature.MmTelCapabilities capabilities)1142     public void onFeatureCapabilityChanged(int networkType,
1143             MmTelFeature.MmTelCapabilities capabilities) {
1144         logger.debug("onFeatureCapabilityChanged networkType=" + networkType
1145                 +", capabilities=" + capabilities);
1146 
1147         Thread thread = new Thread(() -> onFeatureCapabilityChangedInternal(networkType,
1148                 capabilities), "onFeatureCapabilityChangedInternal thread");
1149 
1150         thread.start();
1151     }
1152 
onFeatureCapabilityChangedInternal(int networkType, MmTelFeature.MmTelCapabilities capabilities)1153     synchronized private void onFeatureCapabilityChangedInternal(int networkType,
1154             MmTelFeature.MmTelCapabilities capabilities) {
1155         boolean oldIsVolteAvailable = mIsVolteAvailable;
1156         boolean oldIsVtAvailable = mIsVtAvailable;
1157         boolean oldIsVoWifiAvailable = mIsVoWifiAvailable;
1158         boolean oldIsViWifiAvailable = mIsViWifiAvailable;
1159 
1160         mIsVolteAvailable = (networkType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) &&
1161                 capabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
1162 
1163         mIsVoWifiAvailable = (networkType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) &&
1164                 capabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
1165 
1166         mIsVtAvailable = (networkType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) &&
1167                 capabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
1168 
1169         mIsViWifiAvailable = (networkType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) &&
1170                 capabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
1171 
1172         logger.print("mIsVolteAvailable=" + mIsVolteAvailable +
1173                 " mIsVoWifiAvailable=" + mIsVoWifiAvailable +
1174                 " mIsVtAvailable=" + mIsVtAvailable +
1175                 " mIsViWifiAvailable=" + mIsViWifiAvailable +
1176                 " oldIsVolteAvailable=" + oldIsVolteAvailable +
1177                 " oldIsVoWifiAvailable=" + oldIsVoWifiAvailable +
1178                 " oldIsVtAvailable=" + oldIsVtAvailable +
1179                 " oldIsViWifiAvailable=" + oldIsViWifiAvailable);
1180 
1181         if(oldIsVolteAvailable != mIsVolteAvailable ||
1182                 oldIsVtAvailable != mIsVtAvailable ||
1183                 oldIsVoWifiAvailable != mIsVoWifiAvailable ||
1184                 oldIsViWifiAvailable != mIsViWifiAvailable) {
1185             if(mGotTriggerFromStack) {
1186                 if((Settings.Global.getInt(mContext.getContentResolver(),
1187                         Settings.Global.AIRPLANE_MODE_ON, 0) != 0) && !mIsVoWifiAvailable &&
1188                         !mIsViWifiAvailable) {
1189                     logger.print("Airplane mode was on and no vowifi and viwifi." +
1190                         " Don't need publish. Stack will unpublish");
1191                     return;
1192                 }
1193 
1194                 if(isOnIWLAN()) {
1195                     // will check duplicated PUBLISH in requestPublication by invokePublish
1196                     requestLocalPublish(PublishType.
1197                             PRES_PUBLISH_TRIGGER_FEATURE_AVAILABILITY_CHANGED);
1198                 }
1199             } else {
1200                 mHasCachedTrigger = true;
1201             }
1202         }
1203     }
1204 
isOnLTE()1205     private boolean isOnLTE() {
1206         TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
1207                 Context.TELEPHONY_SERVICE);
1208             int networkType = teleMgr.getDataNetworkType();
1209             logger.debug("mMovedToLTE=" + mMovedToLTE + " networkType=" + networkType);
1210 
1211             // Had reported LTE by trigger and still have DATA.
1212             return mMovedToLTE && (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN);
1213     }
1214 
isOnIWLAN()1215     private boolean isOnIWLAN() {
1216         TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
1217                 Context.TELEPHONY_SERVICE);
1218             int networkType = teleMgr.getDataNetworkType();
1219             logger.debug("mMovedToIWLAN=" + mMovedToIWLAN + " networkType=" + networkType);
1220 
1221             // Had reported IWLAN by trigger and still have DATA.
1222             return mMovedToIWLAN && (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN);
1223     }
1224 }
1225