1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ims.rcs.uce.presence.publish;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.database.ContentObserver;
25 import android.net.Uri;
26 import android.os.Handler;
27 import android.os.HandlerThread;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.provider.Settings;
31 import android.provider.Telephony;
32 import android.telecom.TelecomManager;
33 import android.telephony.AccessNetworkConstants;
34 import android.telephony.AccessNetworkConstants.TransportType;
35 import android.telephony.ims.ImsException;
36 import android.telephony.ims.ImsManager;
37 import android.telephony.ims.ImsMmTelManager;
38 import android.telephony.ims.ImsMmTelManager.CapabilityCallback;
39 import android.telephony.ims.ImsRcsManager;
40 import android.telephony.ims.ImsReasonInfo;
41 import android.telephony.ims.ImsRegistrationAttributes;
42 import android.telephony.ims.ProvisioningManager;
43 import android.telephony.ims.RegistrationManager;
44 import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
45 import android.util.IndentingPrintWriter;
46 import android.util.LocalLog;
47 import android.util.Log;
48 
49 import com.android.ims.rcs.uce.UceStatsWriter;
50 import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControllerCallback;
51 import com.android.ims.rcs.uce.presence.publish.PublishController.PublishTriggerType;
52 import com.android.ims.rcs.uce.util.UceUtils;
53 import com.android.internal.annotations.VisibleForTesting;
54 import com.android.internal.telephony.util.HandlerExecutor;
55 
56 import java.io.PrintWriter;
57 import java.util.ArrayList;
58 import java.util.Arrays;
59 import java.util.Collections;
60 import java.util.List;
61 import java.util.Objects;
62 import java.util.Set;
63 
64 /**
65  * Listen to the device changes and notify the PublishController to publish the device's
66  * capabilities to the Presence server.
67  */
68 public class DeviceCapabilityListener {
69 
70     private static final String LOG_TAG = UceUtils.getLogPrefix() + "DeviceCapListener";
71 
72     private static final long REGISTER_IMS_CHANGED_DELAY = 15000L;  // 15 seconds
73 
74     private final UceStatsWriter mUceStatsWriter;
75 
76     /**
77      * Used to inject ImsMmTelManager instances for testing.
78      */
79     @VisibleForTesting
80     public interface ImsMmTelManagerFactory {
getImsMmTelManager(int subId)81         ImsMmTelManager getImsMmTelManager(int subId);
82     }
83 
84     /**
85      * Used to inject ImsRcsManager instances for testing.
86      */
87     @VisibleForTesting
88     public interface ImsRcsManagerFactory {
getImsRcsManager(int subId)89         ImsRcsManager getImsRcsManager(int subId);
90     }
91 
92     /**
93      * Used to inject ProvisioningManager instances for testing.
94      */
95     @VisibleForTesting
96     public interface ProvisioningManagerFactory {
getProvisioningManager(int subId)97         ProvisioningManager getProvisioningManager(int subId);
98     }
99 
100     /*
101      * Handle registering IMS callback and triggering the publish request because of the
102      * capabilities changed.
103      */
104     private class DeviceCapabilityHandler extends Handler {
105         private static final long TRIGGER_PUBLISH_REQUEST_DELAY_MS = 500L;
106 
107         private static final int EVENT_REGISTER_IMS_CONTENT_CHANGE = 1;
108         private static final int EVENT_UNREGISTER_IMS_CHANGE = 2;
109         private static final int EVENT_REQUEST_PUBLISH = 3;
110 
DeviceCapabilityHandler(Looper looper)111         DeviceCapabilityHandler(Looper looper) {
112             super(looper);
113         }
114 
115         @Override
handleMessage(Message msg)116         public void handleMessage(Message msg) {
117             logd("handleMessage: " + msg.what);
118             if (mIsDestroyed) return;
119             switch (msg.what) {
120                 case EVENT_REGISTER_IMS_CONTENT_CHANGE:
121                     registerImsProvisionCallback();
122                     break;
123                 case EVENT_UNREGISTER_IMS_CHANGE:
124                     unregisterImsProvisionCallback();
125                     break;
126                 case EVENT_REQUEST_PUBLISH:
127                     int triggerType = msg.arg1;
128                     mCallback.requestPublishFromInternal(triggerType);
129                     break;
130             }
131         }
132 
sendRegisterImsContentChangedMessage(long delay)133         public void sendRegisterImsContentChangedMessage(long delay) {
134             // Remove the existing message and send a new one with the delayed time.
135             removeMessages(EVENT_REGISTER_IMS_CONTENT_CHANGE);
136             Message msg = obtainMessage(EVENT_REGISTER_IMS_CONTENT_CHANGE);
137             sendMessageDelayed(msg, delay);
138         }
139 
removeRegisterImsContentChangedMessage()140         public void removeRegisterImsContentChangedMessage() {
141             removeMessages(EVENT_REGISTER_IMS_CONTENT_CHANGE);
142         }
143 
sendUnregisterImsCallbackMessage()144         public void sendUnregisterImsCallbackMessage() {
145             removeMessages(EVENT_REGISTER_IMS_CONTENT_CHANGE);
146             sendEmptyMessage(EVENT_UNREGISTER_IMS_CHANGE);
147         }
148 
sendTriggeringPublishMessage(@ublishTriggerType int type)149         public void sendTriggeringPublishMessage(@PublishTriggerType int type) {
150             logd("sendTriggeringPublishMessage: type=" + type);
151             // Remove the existing message and resend a new message.
152             removeMessages(EVENT_REQUEST_PUBLISH);
153             Message message = obtainMessage();
154             message.what = EVENT_REQUEST_PUBLISH;
155             message.arg1 = type;
156             sendMessageDelayed(message, TRIGGER_PUBLISH_REQUEST_DELAY_MS);
157         }
158     }
159 
160     private final int mSubId;
161     private final Context mContext;
162     private final LocalLog mLocalLog = new LocalLog(UceUtils.LOG_SIZE);
163     private volatile boolean mInitialized;
164     private volatile boolean mIsDestroyed;
165     private volatile boolean mIsRcsConnected;
166     private volatile boolean mIsImsCallbackRegistered;
167 
168     // The callback to trigger the internal publish request
169     private final PublishControllerCallback mCallback;
170     private final DeviceCapabilityInfo mCapabilityInfo;
171     private final HandlerThread mHandlerThread;
172     private final DeviceCapabilityHandler mHandler;
173     private final HandlerExecutor mHandlerExecutor;
174 
175     private ImsMmTelManager mImsMmTelManager;
176     private ImsMmTelManagerFactory mImsMmTelManagerFactory = (subId) -> getImsMmTelManager(subId);
177 
178     private ImsRcsManager mImsRcsManager;
179     private ImsRcsManagerFactory mImsRcsManagerFactory = (subId) -> getImsRcsManager(subId);
180 
181     private ProvisioningManager mProvisioningManager;
182     private ProvisioningManagerFactory mProvisioningMgrFactory = (subId)
183             -> ProvisioningManager.createForSubscriptionId(subId);
184 
185     private ContentObserver mMobileDataObserver = null;
186     private ContentObserver mSimInfoContentObserver = null;
187 
188     private final Object mLock = new Object();
189 
DeviceCapabilityListener(Context context, int subId, DeviceCapabilityInfo info, PublishControllerCallback callback, UceStatsWriter uceStatsWriter)190     public DeviceCapabilityListener(Context context, int subId, DeviceCapabilityInfo info,
191             PublishControllerCallback callback, UceStatsWriter uceStatsWriter) {
192         mSubId = subId;
193         logi("create");
194 
195         mContext = context;
196         mCallback = callback;
197         mCapabilityInfo = info;
198         mInitialized = false;
199         mUceStatsWriter = uceStatsWriter;
200 
201         mHandlerThread = new HandlerThread("DeviceCapListenerThread");
202         mHandlerThread.start();
203         mHandler = new DeviceCapabilityHandler(mHandlerThread.getLooper());
204         mHandlerExecutor = new HandlerExecutor(mHandler);
205     }
206 
207     /**
208      * Turn on the device capabilities changed listener
209      */
initialize()210     public void initialize() {
211         synchronized (mLock) {
212             if (mIsDestroyed) {
213                 logw("initialize: This instance is already destroyed");
214                 return;
215             }
216             if (mInitialized) return;
217 
218             logi("initialize");
219             mImsMmTelManager = mImsMmTelManagerFactory.getImsMmTelManager(mSubId);
220             mImsRcsManager = mImsRcsManagerFactory.getImsRcsManager(mSubId);
221             mProvisioningManager = mProvisioningMgrFactory.getProvisioningManager(mSubId);
222             registerReceivers();
223             registerImsProvisionCallback();
224 
225             mInitialized = true;
226         }
227     }
228 
229     // The RcsFeature has been connected to the framework
onRcsConnected()230     public void onRcsConnected() {
231         mIsRcsConnected = true;
232         mHandler.sendRegisterImsContentChangedMessage(0L);
233     }
234 
235     // The framework has lost the binding to the RcsFeature.
onRcsDisconnected()236     public void onRcsDisconnected() {
237         mIsRcsConnected = false;
238         mHandler.sendUnregisterImsCallbackMessage();
239     }
240 
241     /**
242      * Notify the instance is destroyed
243      */
onDestroy()244     public void onDestroy() {
245         logi("onDestroy");
246         mIsDestroyed = true;
247         synchronized (mLock) {
248             if (!mInitialized) return;
249             logi("turnOffListener");
250             mInitialized = false;
251             unregisterReceivers();
252             unregisterImsProvisionCallback();
253             mHandlerThread.quit();
254         }
255     }
256 
257     /*
258      * Register receivers to listen to the data changes.
259      */
registerReceivers()260     private void registerReceivers() {
261         logd("registerReceivers");
262         IntentFilter filter = new IntentFilter();
263         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
264         filter.addAction(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
265         mContext.registerReceiver(mReceiver, filter);
266 
267         ContentResolver resolver = mContext.getContentResolver();
268         if (resolver != null) {
269             // Listen to the mobile data content changed.
270             resolver.registerContentObserver(
271                     Settings.Global.getUriFor(Settings.Global.MOBILE_DATA), false,
272                     getMobileDataObserver());
273             // Listen to the SIM info content changed.
274             resolver.registerContentObserver(Telephony.SimInfo.CONTENT_URI, false,
275                     getSimInfoContentObserver());
276         }
277     }
278 
unregisterReceivers()279     private void unregisterReceivers() {
280         logd("unregisterReceivers");
281         mContext.unregisterReceiver(mReceiver);
282         ContentResolver resolver = mContext.getContentResolver();
283         if (resolver != null) {
284             resolver.unregisterContentObserver(getMobileDataObserver());
285             resolver.unregisterContentObserver(getSimInfoContentObserver());
286         }
287     }
288 
registerImsProvisionCallback()289     private void registerImsProvisionCallback() {
290         if (mIsImsCallbackRegistered) {
291             logd("registerImsProvisionCallback: already registered.");
292             return;
293         }
294 
295         logd("registerImsProvisionCallback");
296         try {
297             // Register mmtel callback
298             if (mImsMmTelManager != null) {
299                 mImsMmTelManager.registerImsRegistrationCallback(mHandlerExecutor,
300                         mMmtelRegistrationCallback);
301                 mImsMmTelManager.registerMmTelCapabilityCallback(mHandlerExecutor,
302                         mMmtelCapabilityCallback);
303             }
304 
305             // Register rcs callback
306             if (mImsRcsManager != null) {
307                 mImsRcsManager.registerImsRegistrationCallback(mHandlerExecutor,
308                         mRcsRegistrationCallback);
309             }
310 
311             // Register provisioning changed callback
312             mProvisioningManager.registerProvisioningChangedCallback(mHandlerExecutor,
313                     mProvisionChangedCallback);
314 
315             // Set the IMS callback is registered.
316             mIsImsCallbackRegistered = true;
317         } catch (ImsException e) {
318             logw("registerImsProvisionCallback error: " + e);
319             // Unregister the callback
320             unregisterImsProvisionCallback();
321 
322             // Retry registering IMS callback only when the RCS is connected.
323             if (mIsRcsConnected) {
324                 mHandler.sendRegisterImsContentChangedMessage(REGISTER_IMS_CHANGED_DELAY);
325             }
326         }
327     }
328 
unregisterImsProvisionCallback()329     private void unregisterImsProvisionCallback() {
330         logd("unregisterImsProvisionCallback");
331 
332         // Clear the registering IMS callback message from the handler thread
333         mHandler.removeRegisterImsContentChangedMessage();
334 
335         // Unregister mmtel callback
336         if (mImsMmTelManager != null) {
337             try {
338                 mImsMmTelManager.unregisterImsRegistrationCallback(mMmtelRegistrationCallback);
339             } catch (RuntimeException e) {
340                 logw("unregister MMTel registration error: " + e.getMessage());
341             }
342             try {
343                 mImsMmTelManager.unregisterMmTelCapabilityCallback(mMmtelCapabilityCallback);
344             } catch (RuntimeException e) {
345                 logw("unregister MMTel capability error: " + e.getMessage());
346             }
347         }
348 
349         // Unregister rcs callback
350         if (mImsRcsManager != null) {
351             try {
352                 mImsRcsManager.unregisterImsRegistrationCallback(mRcsRegistrationCallback);
353             } catch (RuntimeException e) {
354                 logw("unregister rcs capability error: " + e.getMessage());
355             }
356         }
357 
358         try {
359             // Unregister provisioning changed callback
360             mProvisioningManager.unregisterProvisioningChangedCallback(mProvisionChangedCallback);
361         } catch (RuntimeException e) {
362             logw("unregister provisioning callback error: " + e.getMessage());
363         }
364 
365         // Clear the IMS callback registered flag.
366         mIsImsCallbackRegistered = false;
367     }
368 
369     @VisibleForTesting
370     public final BroadcastReceiver mReceiver = new BroadcastReceiver() {
371         @Override
372         public void onReceive(Context context, Intent intent) {
373             if (intent == null || intent.getAction() == null) return;
374             switch (intent.getAction()) {
375                 case TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED:
376                     int preferredMode = intent.getIntExtra(TelecomManager.EXTRA_TTY_PREFERRED_MODE,
377                             TelecomManager.TTY_MODE_OFF);
378                     handleTtyPreferredModeChanged(preferredMode);
379                     break;
380 
381                 case Intent.ACTION_AIRPLANE_MODE_CHANGED:
382                     boolean airplaneMode = intent.getBooleanExtra("state", false);
383                     handleAirplaneModeChanged(airplaneMode);
384                     break;
385             }
386         }
387     };
388 
getMobileDataObserver()389     private ContentObserver getMobileDataObserver() {
390         synchronized (mLock) {
391             if (mMobileDataObserver == null) {
392                 mMobileDataObserver = new ContentObserver(new Handler(mHandler.getLooper())) {
393                     @Override
394                     public void onChange(boolean selfChange) {
395                         boolean isEnabled = Settings.Global.getInt(mContext.getContentResolver(),
396                                 Settings.Global.MOBILE_DATA, 1) == 1;
397                         handleMobileDataChanged(isEnabled);
398                     }
399                 };
400             }
401             return mMobileDataObserver;
402         }
403     }
404 
getSimInfoContentObserver()405     private ContentObserver getSimInfoContentObserver() {
406         synchronized (mLock) {
407             if (mSimInfoContentObserver == null) {
408                 mSimInfoContentObserver = new ContentObserver(new Handler(mHandler.getLooper())) {
409                     @Override
410                     public void onChange(boolean selfChange) {
411                         if (mImsMmTelManager == null) {
412                             logw("SimInfo change error: MmTelManager is null");
413                             return;
414                         }
415 
416                         try {
417                             boolean isEnabled = mImsMmTelManager.isVtSettingEnabled();
418                             handleVtSettingChanged(isEnabled);
419                         } catch (RuntimeException e) {
420                             logw("SimInfo change error: " + e);
421                         }
422                     }
423                 };
424             }
425             return mSimInfoContentObserver;
426         }
427     }
428 
getImsMmTelManager(int subId)429     private ImsMmTelManager getImsMmTelManager(int subId) {
430         try {
431             ImsManager imsManager = mContext.getSystemService(
432                     android.telephony.ims.ImsManager.class);
433             return (imsManager == null) ? null : imsManager.getImsMmTelManager(subId);
434         } catch (IllegalArgumentException e) {
435             logw("getImsMmTelManager error: " + e.getMessage());
436             return null;
437         }
438     }
439 
getImsRcsManager(int subId)440     private ImsRcsManager getImsRcsManager(int subId) {
441         try {
442             ImsManager imsManager = mContext.getSystemService(
443                     android.telephony.ims.ImsManager.class);
444             return (imsManager == null) ? null : imsManager.getImsRcsManager(subId);
445         } catch (IllegalArgumentException e) {
446             logw("getImsRcsManager error: " + e.getMessage());
447             return null;
448         }
449     }
450 
451     @VisibleForTesting
452     public final RegistrationManager.RegistrationCallback mRcsRegistrationCallback =
453             new RegistrationManager.RegistrationCallback() {
454                 @Override
455                 public void onRegistered(ImsRegistrationAttributes attributes) {
456                     synchronized (mLock) {
457                         logi("onRcsRegistered: " + attributes);
458                         if (!mIsImsCallbackRegistered) return;
459 
460                         List<String> featureTagList = new ArrayList<>(attributes.getFeatureTags());
461                         int registrationTech = attributes.getRegistrationTechnology();
462 
463                         mUceStatsWriter.setImsRegistrationFeatureTagStats(
464                                 mSubId, featureTagList, registrationTech);
465                         handleImsRcsRegistered(attributes);
466                     }
467                 }
468 
469                 @Override
470                 public void onUnregistered(ImsReasonInfo info) {
471                     synchronized (mLock) {
472                         logi("onRcsUnregistered: " + info);
473                         if (!mIsImsCallbackRegistered) return;
474                         mUceStatsWriter.setStoreCompleteImsRegistrationFeatureTagStats(mSubId);
475                         handleImsRcsUnregistered();
476                     }
477                 }
478 
479                 @Override
480                 public void onSubscriberAssociatedUriChanged(Uri[] uris) {
481                     synchronized (mLock) {
482                         logi("onRcsSubscriberAssociatedUriChanged");
483                         handleRcsSubscriberAssociatedUriChanged(uris, true);
484                     }
485                 }
486     };
487 
488     @VisibleForTesting
489     public final RegistrationManager.RegistrationCallback mMmtelRegistrationCallback =
490             new RegistrationManager.RegistrationCallback() {
491                 @Override
492                 public void onRegistered(@TransportType int transportType) {
493                     synchronized (mLock) {
494                         String type = AccessNetworkConstants.transportTypeToString(transportType);
495                         logi("onMmTelRegistered: " + type);
496                         if (!mIsImsCallbackRegistered) return;
497                         handleImsMmtelRegistered(transportType);
498                     }
499                 }
500 
501                 @Override
502                 public void onUnregistered(ImsReasonInfo info) {
503                     synchronized (mLock) {
504                         logi("onMmTelUnregistered: " + info);
505                         if (!mIsImsCallbackRegistered) return;
506                         handleImsMmtelUnregistered();
507                     }
508                 }
509 
510                 @Override
511                 public void onSubscriberAssociatedUriChanged(Uri[] uris) {
512                     synchronized (mLock) {
513                         logi("onMmTelSubscriberAssociatedUriChanged");
514                         handleMmTelSubscriberAssociatedUriChanged(uris, true);
515                     }
516                 }
517             };
518 
519     @VisibleForTesting
520     public final ImsMmTelManager.CapabilityCallback mMmtelCapabilityCallback =
521             new CapabilityCallback() {
522                 @Override
523                 public void onCapabilitiesStatusChanged(MmTelCapabilities capabilities) {
524                     if (capabilities == null) {
525                         logw("onCapabilitiesStatusChanged: parameter is null");
526                         return;
527                     }
528                     synchronized (mLock) {
529                         handleMmtelCapabilitiesStatusChanged(capabilities);
530                     }
531                 }
532             };
533 
534     @VisibleForTesting
535     public final ProvisioningManager.Callback mProvisionChangedCallback =
536             new ProvisioningManager.Callback() {
537                 @Override
538                 public void onProvisioningIntChanged(int item, int value) {
539                     logi("onProvisioningIntChanged: item=" + item + ", value=" + value);
540                     switch (item) {
541                         case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
542                         case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
543                         case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
544                             handleProvisioningChanged();
545                         case ProvisioningManager.KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS:
546                             handlePublishThrottleChanged(value);
547                             break;
548                     }
549                 }
550             };
551 
handleTtyPreferredModeChanged(int preferredMode)552     private void handleTtyPreferredModeChanged(int preferredMode) {
553         boolean isChanged = mCapabilityInfo.updateTtyPreferredMode(preferredMode);
554         logi("TTY preferred mode changed: " + preferredMode + ", isChanged=" + isChanged);
555         if (isChanged) {
556             mHandler.sendTriggeringPublishMessage(
557                 PublishController.PUBLISH_TRIGGER_TTY_PREFERRED_CHANGE);
558         }
559     }
560 
handleAirplaneModeChanged(boolean state)561     private void handleAirplaneModeChanged(boolean state) {
562         boolean isChanged = mCapabilityInfo.updateAirplaneMode(state);
563         logi("Airplane mode changed: " + state + ", isChanged="+ isChanged);
564         if (isChanged) {
565             mHandler.sendTriggeringPublishMessage(
566                     PublishController.PUBLISH_TRIGGER_AIRPLANE_MODE_CHANGE);
567         }
568     }
569 
handleMobileDataChanged(boolean isEnabled)570     private void handleMobileDataChanged(boolean isEnabled) {
571         boolean isChanged = mCapabilityInfo.updateMobileData(isEnabled);
572         logi("Mobile data changed: " + isEnabled + ", isChanged=" + isChanged);
573         if (isChanged) {
574             mHandler.sendTriggeringPublishMessage(
575                     PublishController.PUBLISH_TRIGGER_MOBILE_DATA_CHANGE);
576         }
577     }
578 
handleVtSettingChanged(boolean isEnabled)579     private void handleVtSettingChanged(boolean isEnabled) {
580         boolean isChanged = mCapabilityInfo.updateVtSetting(isEnabled);
581         logi("VT setting changed: " + isEnabled + ", isChanged=" + isChanged);
582         if (isChanged) {
583             mHandler.sendTriggeringPublishMessage(
584                     PublishController.PUBLISH_TRIGGER_VT_SETTING_CHANGE);
585         }
586     }
587 
588     /*
589      * This method is called when the MMTEL is registered.
590      */
handleImsMmtelRegistered(int imsTransportType)591     private void handleImsMmtelRegistered(int imsTransportType) {
592         mCapabilityInfo.updateImsMmtelRegistered(imsTransportType);
593         mHandler.sendTriggeringPublishMessage(
594                 PublishController.PUBLISH_TRIGGER_MMTEL_REGISTERED);
595     }
596 
597     /*
598      * This method is called when the MMTEL is unregistered.
599      */
handleImsMmtelUnregistered()600     private void handleImsMmtelUnregistered() {
601         mCapabilityInfo.updateImsMmtelUnregistered();
602         // When the MMTEL is unregistered, the mmtel associated uri should be cleared.
603         handleMmTelSubscriberAssociatedUriChanged(null, false);
604         mHandler.sendTriggeringPublishMessage(
605                 PublishController.PUBLISH_TRIGGER_MMTEL_UNREGISTERED);
606     }
607 
608     /*
609      * This method is called when the MMTEL associated uri has changed.
610      */
handleMmTelSubscriberAssociatedUriChanged(Uri[] uris, boolean triggerPublish)611     private void handleMmTelSubscriberAssociatedUriChanged(Uri[] uris, boolean triggerPublish) {
612         Uri originalUri = mCapabilityInfo.getMmtelAssociatedUri();
613         mCapabilityInfo.updateMmTelAssociatedUri(uris);
614         Uri currentUri = mCapabilityInfo.getMmtelAssociatedUri();
615 
616         boolean hasChanged = !(Objects.equals(originalUri, currentUri));
617         logi("handleMmTelSubscriberAssociatedUriChanged: triggerPublish=" + triggerPublish +
618                 ", hasChanged=" + hasChanged);
619 
620         if (triggerPublish && hasChanged) {
621             mHandler.sendTriggeringPublishMessage(
622                     PublishController.PUBLISH_TRIGGER_MMTEL_URI_CHANGE);
623         }
624     }
625 
handleMmtelCapabilitiesStatusChanged(MmTelCapabilities capabilities)626     private void handleMmtelCapabilitiesStatusChanged(MmTelCapabilities capabilities) {
627         boolean isChanged = mCapabilityInfo.updateMmtelCapabilitiesChanged(capabilities);
628         logi("MMTel capabilities status changed: isChanged=" + isChanged);
629         if (isChanged) {
630             mHandler.sendTriggeringPublishMessage(
631                     PublishController.PUBLISH_TRIGGER_MMTEL_CAPABILITY_CHANGE);
632         }
633     }
634 
635     /*
636      * This method is called when RCS is registered.
637      */
handleImsRcsRegistered(ImsRegistrationAttributes attr)638     private void handleImsRcsRegistered(ImsRegistrationAttributes attr) {
639         if (mCapabilityInfo.updateImsRcsRegistered(attr)) {
640             mHandler.sendTriggeringPublishMessage(PublishController.PUBLISH_TRIGGER_RCS_REGISTERED);
641         }
642     }
643 
644     /*
645      * This method is called when RCS is unregistered.
646      */
handleImsRcsUnregistered()647     private void handleImsRcsUnregistered() {
648         boolean hasChanged = mCapabilityInfo.updateImsRcsUnregistered();
649         // When the RCS is unregistered, the rcs associated uri should be cleared.
650         handleRcsSubscriberAssociatedUriChanged(null, false);
651         // Trigger publish if the state has changed.
652         if (hasChanged) {
653             mHandler.sendTriggeringPublishMessage(
654                     PublishController.PUBLISH_TRIGGER_RCS_UNREGISTERED);
655         }
656     }
657 
658     /*
659      * This method is called when the RCS associated uri has changed.
660      */
handleRcsSubscriberAssociatedUriChanged(Uri[] uris, boolean triggerPublish)661     private void handleRcsSubscriberAssociatedUriChanged(Uri[] uris, boolean triggerPublish) {
662         Uri originalUri = mCapabilityInfo.getRcsAssociatedUri();
663         mCapabilityInfo.updateRcsAssociatedUri(uris);
664         Uri currentUri = mCapabilityInfo.getRcsAssociatedUri();
665 
666         boolean hasChanged = !(Objects.equals(originalUri, currentUri));
667         logi("handleRcsSubscriberAssociatedUriChanged: triggerPublish=" + triggerPublish +
668                 ", hasChanged=" + hasChanged);
669 
670         if (triggerPublish && hasChanged) {
671             mHandler.sendTriggeringPublishMessage(PublishController.PUBLISH_TRIGGER_RCS_URI_CHANGE);
672         }
673     }
674 
675     /*
676      * This method is called when the provisioning is changed
677      */
handleProvisioningChanged()678     private void handleProvisioningChanged() {
679         mHandler.sendTriggeringPublishMessage(
680                 PublishController.PUBLISH_TRIGGER_PROVISIONING_CHANGE);
681     }
682 
683     /*
684      * Update the publish throttle.
685      */
handlePublishThrottleChanged(int value)686     private void handlePublishThrottleChanged(int value) {
687         mCallback.updatePublishThrottle(value);
688     }
689 
690     @VisibleForTesting
getHandler()691     public Handler getHandler() {
692         return mHandler;
693     }
694 
695     @VisibleForTesting
setImsMmTelManagerFactory(ImsMmTelManagerFactory factory)696     public void setImsMmTelManagerFactory(ImsMmTelManagerFactory factory) {
697         mImsMmTelManagerFactory = factory;
698     }
699 
700     @VisibleForTesting
setImsRcsManagerFactory(ImsRcsManagerFactory factory)701     public void setImsRcsManagerFactory(ImsRcsManagerFactory factory) {
702         mImsRcsManagerFactory = factory;
703     }
704 
705     @VisibleForTesting
setProvisioningMgrFactory(ProvisioningManagerFactory factory)706     public void setProvisioningMgrFactory(ProvisioningManagerFactory factory) {
707         mProvisioningMgrFactory = factory;
708     }
709 
710     @VisibleForTesting
setImsCallbackRegistered(boolean registered)711     public void setImsCallbackRegistered(boolean registered) {
712         mIsImsCallbackRegistered = registered;
713     }
714 
logd(String log)715     private void logd(String log) {
716         Log.d(LOG_TAG, getLogPrefix().append(log).toString());
717         mLocalLog.log("[D] " + log);
718     }
719 
logi(String log)720     private void logi(String log) {
721         Log.i(LOG_TAG, getLogPrefix().append(log).toString());
722         mLocalLog.log("[I] " + log);
723     }
724 
logw(String log)725     private void logw(String log) {
726         Log.w(LOG_TAG, getLogPrefix().append(log).toString());
727         mLocalLog.log("[W] " + log);
728     }
729 
getLogPrefix()730     private StringBuilder getLogPrefix() {
731         StringBuilder builder = new StringBuilder("[");
732         builder.append(mSubId);
733         builder.append("] ");
734         return builder;
735     }
736 
dump(PrintWriter printWriter)737     public void dump(PrintWriter printWriter) {
738         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
739         pw.println("DeviceCapListener" + "[subId: " + mSubId + "]:");
740         pw.increaseIndent();
741 
742         mCapabilityInfo.dump(pw);
743 
744         pw.println("Log:");
745         pw.increaseIndent();
746         mLocalLog.dump(pw);
747         pw.decreaseIndent();
748         pw.println("---");
749 
750         pw.decreaseIndent();
751     }
752 }
753