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