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;
30 
31 import android.app.Service;
32 import android.content.BroadcastReceiver;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.database.ContentObserver;
38 import android.net.ConnectivityManager;
39 import android.os.Handler;
40 import android.os.IBinder;
41 import android.os.Looper;
42 import android.os.RemoteException;
43 import android.os.ServiceManager;
44 import android.provider.Settings;
45 import android.provider.Telephony;
46 import android.telecom.TelecomManager;
47 import android.telephony.AccessNetworkConstants;
48 import android.telephony.SubscriptionManager;
49 import android.telephony.ims.ImsException;
50 import android.telephony.ims.ImsMmTelManager;
51 import android.telephony.ims.ImsReasonInfo;
52 import android.telephony.ims.ProvisioningManager;
53 import android.telephony.ims.RcsContactUceCapability;
54 import android.telephony.ims.RegistrationManager;
55 import android.telephony.ims.feature.MmTelFeature;
56 
57 import com.android.ims.IRcsPresenceListener;
58 import com.android.ims.RcsPresenceInfo;
59 import com.android.ims.ResultCode;
60 import com.android.ims.RcsPresence;
61 import com.android.ims.internal.IRcsPresence;
62 import com.android.ims.internal.IRcsService;
63 import com.android.ims.internal.Logger;
64 import com.android.service.ims.R;
65 import com.android.service.ims.presence.ContactCapabilityResponse;
66 import com.android.service.ims.presence.PresenceBase;
67 import com.android.service.ims.presence.PresencePublication;
68 import com.android.service.ims.presence.PresenceSubscriber;
69 
70 import java.util.ArrayList;
71 import java.util.List;
72 import java.util.stream.Collectors;
73 
74 public class RcsService extends Service {
75 
76     private static final int IMS_SERVICE_RETRY_TIMEOUT_MS = 5000;
77 
78     private Logger logger = Logger.getLogger(this.getClass().getName());
79 
80     private RcsStackAdaptor mRcsStackAdaptor = null;
81     private PresencePublication mPublication = null;
82     private PresenceSubscriber mSubscriber = null;
83 
84     private Handler mRetryHandler;
85     private Runnable mRegisterCallbacks = this::registerImsCallbacksAndSetAssociatedSubscription;
86     private int mNetworkRegistrationType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
87 
88     private int mAssociatedSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
89 
90     private SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener
91             = new SubscriptionManager.OnSubscriptionsChangedListener() {
92         @Override
93         public void onSubscriptionsChanged() {
94             registerImsCallbacksAndSetAssociatedSubscription();
95         }
96     };
97 
98     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
99         @Override
100         public void onReceive(Context context, Intent intent) {
101             switch (intent.getAction()) {
102                 case TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED: {
103                     int newPreferredTtyMode = intent.getIntExtra(
104                             TelecomManager.EXTRA_TTY_PREFERRED_MODE,
105                             TelecomManager.TTY_MODE_OFF);
106                     if (mPublication != null) {
107                         mPublication.onTtyPreferredModeChanged(newPreferredTtyMode);
108                     }
109                     break;
110                 }
111                 case Intent.ACTION_AIRPLANE_MODE_CHANGED: {
112                     boolean airplaneMode = intent.getBooleanExtra("state", false);
113                     if (mPublication != null) {
114                         mPublication.onAirplaneModeChanged(airplaneMode);
115                     }
116                     break;
117                 }
118                 case SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED: {
119                     mRetryHandler.removeCallbacks(mRegisterCallbacks);
120                     mRetryHandler.post(mRegisterCallbacks);
121                 }
122             }
123         }
124     };
125 
126     private class CapabilityResultListener implements ContactCapabilityResponse {
127 
128         private final IRcsPresenceListener mListener;
129 
CapabilityResultListener(IRcsPresenceListener listener)130         public CapabilityResultListener(IRcsPresenceListener listener) {
131             mListener = listener;
132         }
133 
134         @Override
onSuccess(int reqId)135         public void onSuccess(int reqId) {
136             try {
137                 mListener.onSuccess(reqId);
138             } catch (RemoteException e) {
139                 logger.warn("CapabilityResultListener: onSuccess exception = " + e.getMessage());
140             }
141         }
142 
143         @Override
onError(int reqId, int resultCode)144         public void onError(int reqId, int resultCode) {
145             try {
146                 mListener.onError(reqId, resultCode);
147             } catch (RemoteException e) {
148                 logger.warn("CapabilityResultListener: onError exception = " + e.getMessage());
149             }
150         }
151 
152         @Override
onFinish(int reqId)153         public void onFinish(int reqId) {
154             try {
155                 mListener.onFinish(reqId);
156             } catch (RemoteException e) {
157                 logger.warn("CapabilityResultListener: onFinish exception = " + e.getMessage());
158             }
159         }
160 
161         @Override
onTimeout(int reqId)162         public void onTimeout(int reqId) {
163             try {
164                 mListener.onTimeout(reqId);
165             } catch (RemoteException e) {
166                 logger.warn("CapabilityResultListener: onTimeout exception = " + e.getMessage());
167             }
168         }
169 
170         @Override
onCapabilitiesUpdated(int reqId, List<RcsContactUceCapability> contactCapabilities, boolean updateLastTimestamp)171         public void onCapabilitiesUpdated(int reqId,
172                 List<RcsContactUceCapability> contactCapabilities, boolean updateLastTimestamp) {
173             ArrayList<RcsPresenceInfo> presenceInfoList = contactCapabilities.stream().map(
174                     PresenceInfoParser::getRcsPresenceInfo).collect(
175                     Collectors.toCollection(ArrayList::new));
176 
177             logger.debug("capabilities updated:");
178             for (RcsPresenceInfo info : presenceInfoList) {
179                 logger.debug("capabilities updated: info -" + info);
180             }
181             // For some reason it uses an intent to send this info back instead of just using the
182             // active binder...
183             Intent intent = new Intent(RcsPresence.ACTION_PRESENCE_CHANGED);
184             intent.putParcelableArrayListExtra(RcsPresence.EXTRA_PRESENCE_INFO_LIST,
185                     presenceInfoList);
186             intent.putExtra("updateLastTimestamp", updateLastTimestamp);
187             launchPersistService(intent);
188         }
189     }
190 
launchPersistService(Intent intent)191     private void launchPersistService(Intent intent) {
192         ComponentName component = new ComponentName("com.android.service.ims.presence",
193                 "com.android.service.ims.presence.PersistService");
194         intent.setComponent(component);
195         startService(intent);
196     }
197 
198     @Override
onCreate()199     public void onCreate() {
200         super.onCreate();
201 
202         logger.debug("RcsService onCreate");
203 
204         mRcsStackAdaptor = RcsStackAdaptor.getInstance(this);
205 
206         mPublication = new PresencePublication(mRcsStackAdaptor, this,
207                 getResources().getStringArray(
208                         R.array.config_volte_provision_error_on_publish_response),
209                 getResources().getStringArray(
210                         R.array.config_rcs_provision_error_on_publish_response));
211         mRcsStackAdaptor.getListener().setPresencePublication(mPublication);
212 
213         mSubscriber = new PresenceSubscriber(mRcsStackAdaptor, this,
214                 getResources().getStringArray(
215                         R.array.config_volte_provision_error_on_subscribe_response),
216                 getResources().getStringArray(
217                         R.array.config_rcs_provision_error_on_subscribe_response));
218         mRcsStackAdaptor.getListener().setPresenceSubscriber(mSubscriber);
219         mPublication.setSubscriber(mSubscriber);
220 
221         final ConnectivityManager cm = getSystemService(ConnectivityManager.class);
222         if (cm != null) {
223             boolean enabled = Settings.Global.getInt(getContentResolver(),
224                     Settings.Global.MOBILE_DATA, 1) == 1;
225             logger.debug("Mobile data enabled status: " + (enabled ? "ON" : "OFF"));
226 
227             onMobileDataEnabled(enabled);
228         }
229 
230         // TODO: support MSIM
231         ServiceManager.addService("rcs", mBinder);
232 
233         mObserver = new MobileDataContentObserver();
234         getContentResolver().registerContentObserver(
235                 Settings.Global.getUriFor(Settings.Global.MOBILE_DATA),
236                 false, mObserver);
237 
238         mSiminfoSettingObserver = new SimInfoContentObserver();
239         getContentResolver().registerContentObserver(Telephony.SimInfo.CONTENT_URI, false,
240                 mSiminfoSettingObserver);
241 
242         mRetryHandler = new Handler(Looper.getMainLooper());
243         registerBroadcastReceiver();
244         registerSubscriptionChangedListener();
245     }
246 
registerBroadcastReceiver()247     private void registerBroadcastReceiver() {
248         IntentFilter filter = new IntentFilter(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
249         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
250         filter.addAction(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
251         registerReceiver(mReceiver, filter);
252     }
253 
unregisterBroadcastReceiver()254     private void unregisterBroadcastReceiver() {
255         unregisterReceiver(mReceiver);
256     }
257 
registerSubscriptionChangedListener()258     private void registerSubscriptionChangedListener() {
259         SubscriptionManager subscriptionManager = getSystemService(SubscriptionManager.class);
260         if (subscriptionManager != null) {
261             // This will call back after the listener is added automatically.
262             subscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
263         } else {
264             logger.error("SubscriptionManager not available! Retrying...");
265             // Retry this again after some time.
266             mRetryHandler.postDelayed(this::registerSubscriptionChangedListener,
267                     IMS_SERVICE_RETRY_TIMEOUT_MS);
268         }
269     }
270 
registerImsCallbacksAndSetAssociatedSubscription()271     private void registerImsCallbacksAndSetAssociatedSubscription() {
272         SubscriptionManager sm = getSystemService(SubscriptionManager.class);
273         if (sm == null) {
274             logger.warn("handleSubscriptionsChanged: SubscriptionManager is null!");
275             return;
276         }
277         int defaultSub = RcsSettingUtils.getDefaultSubscriptionId(this);
278         // If the presence SIP PUBLISH procedure is not supported, treat it as if there is no valid
279         // associated sub
280         if (!RcsSettingUtils.isPublishEnabled(this, defaultSub)) {
281             defaultSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
282         }
283 
284         try {
285             if (defaultSub == mAssociatedSubscription) {
286                 // Don't register duplicate callbacks for the same subscription.
287                 return;
288             }
289             if (mAssociatedSubscription != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
290                 // Get rid of any existing registrations.
291                 ImsMmTelManager oldImsManager = ImsMmTelManager.createForSubscriptionId(
292                         mAssociatedSubscription);
293                 ProvisioningManager oldProvisioningManager =
294                         ProvisioningManager.createForSubscriptionId(mAssociatedSubscription);
295                 oldImsManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
296                 oldImsManager.unregisterMmTelCapabilityCallback(mCapabilityCallback);
297                 oldProvisioningManager.unregisterProvisioningChangedCallback(
298                         mProvisioningChangedCallback);
299                 logger.print("callbacks unregistered for sub " + mAssociatedSubscription);
300             }
301             if (SubscriptionManager.isValidSubscriptionId(defaultSub)) {
302                 ImsMmTelManager imsManager = ImsMmTelManager.createForSubscriptionId(defaultSub);
303                 ProvisioningManager provisioningManager =
304                         ProvisioningManager.createForSubscriptionId(defaultSub);
305                 // move over registrations if the new sub id is valid.
306                 imsManager.registerImsRegistrationCallback(getMainExecutor(),
307                         mImsRegistrationCallback);
308                 imsManager.registerMmTelCapabilityCallback(getMainExecutor(), mCapabilityCallback);
309                 provisioningManager.registerProvisioningChangedCallback(getMainExecutor(),
310                         mProvisioningChangedCallback);
311                 mAssociatedSubscription = defaultSub;
312                 logger.print("callbacks registered for sub " + mAssociatedSubscription);
313             } else {
314                 mAssociatedSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
315             }
316             logger.print("registerImsCallbacksAndSetAssociatedSubscription: new default="
317                     + mAssociatedSubscription);
318             handleAssociatedSubscriptionChanged(mAssociatedSubscription);
319         } catch (ImsException e) {
320             logger.info("Couldn't register callbacks for " + defaultSub + ": "
321                     + e.getMessage());
322             if (e.getCode() == ImsException.CODE_ERROR_SERVICE_UNAVAILABLE) {
323                 handleAssociatedSubscriptionChanged(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
324                 // IMS temporarily unavailable. Retry after a few seconds.
325                 mRetryHandler.removeCallbacks(mRegisterCallbacks);
326                 mRetryHandler.postDelayed(mRegisterCallbacks, IMS_SERVICE_RETRY_TIMEOUT_MS);
327             }
328         }
329     }
330 
handleAssociatedSubscriptionChanged(int newSubId)331     private void handleAssociatedSubscriptionChanged(int newSubId) {
332         if (mSubscriber != null) {
333             mSubscriber.handleAssociatedSubscriptionChanged(newSubId);
334         }
335         if (mPublication != null) {
336             mPublication.handleAssociatedSubscriptionChanged(newSubId);
337         }
338         if (mRcsStackAdaptor != null) {
339             mRcsStackAdaptor.handleAssociatedSubscriptionChanged(newSubId);
340         }
341     }
342 
343     @Override
onStartCommand(Intent intent, int flags, int startId)344     public int onStartCommand(Intent intent, int flags, int startId) {
345         logger.debug("RcsService onStartCommand");
346 
347         return super.onStartCommand(intent, flags, startId);
348     }
349 
350     /**
351       * Cleans up when the service is destroyed
352       */
353     @Override
onDestroy()354     public void onDestroy() {
355         unregisterBroadcastReceiver();
356         getContentResolver().unregisterContentObserver(mObserver);
357         getContentResolver().unregisterContentObserver(mSiminfoSettingObserver);
358         getSystemService(SubscriptionManager.class)
359                 .removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
360 
361         mRcsStackAdaptor.finish();
362         mPublication = null;
363         mSubscriber = null;
364 
365         logger.debug("RcsService onDestroy");
366         super.onDestroy();
367     }
368 
getPublication()369     public PresencePublication getPublication() {
370         return mPublication;
371     }
372 
373     IRcsPresence.Stub mIRcsPresenceImpl = new IRcsPresence.Stub() {
374         /**
375          * Asyncrhonously request the latest capability for a given contact list.
376          * The result will be saved to DB directly if the contactNumber can be found in DB.
377          * And then send intent com.android.ims.presence.CAPABILITY_STATE_CHANGED to notify it.
378          * @param contactsNumber the contact list which will request capability.
379          *                       Currently only support phone number.
380          * @param listener the listener to get the response.
381          * @return the resultCode which is defined by ResultCode.
382          * @note framework uses only.
383          * @hide
384          */
385         public int requestCapability(List<String> contactsNumber,
386             IRcsPresenceListener listener){
387             logger.debug("calling requestCapability");
388             if(mSubscriber == null){
389                 logger.debug("requestCapability, mPresenceSubscriber == null");
390                 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
391             }
392 
393             return mSubscriber.requestCapability(contactsNumber,
394                     new CapabilityResultListener(listener));
395          }
396 
397         /**
398          * Asyncrhonously request the latest presence for a given contact.
399          * The result will be saved to DB directly if it can be found in DB. And then send intent
400          * com.android.ims.presence.AVAILABILITY_STATE_CHANGED to notify it.
401          * @param contactNumber the contact which will request available.
402          *                       Currently only support phone number.
403          * @param listener the listener to get the response.
404          * @return the resultCode which is defined by ResultCode.
405          * @note framework uses only.
406          * @hide
407          */
408         public int requestAvailability(String contactNumber, IRcsPresenceListener listener){
409             if(mSubscriber == null){
410                 logger.error("requestAvailability, mPresenceSubscriber is null");
411                 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
412             }
413 
414             // check availability cache (in RAM).
415             return mSubscriber.requestAvailability(contactNumber,
416                     new CapabilityResultListener(listener), false);
417         }
418 
419         /**
420          * Same as requestAvailability. but requestAvailability will consider throttle to avoid too
421          * fast call. Which means it will not send the request to network in next 60s for the same
422          * request.
423          * The error code SUBSCRIBE_TOO_FREQUENTLY will be returned under the case.
424          * But for this funcation it will always send the request to network.
425          *
426          * @see IRcsPresenceListener
427          * @see RcsManager.ResultCode
428          * @see ResultCode.SUBSCRIBE_TOO_FREQUENTLY
429          */
430         public int requestAvailabilityNoThrottle(String contactNumber,
431                 IRcsPresenceListener listener) {
432             if(mSubscriber == null){
433                 logger.error("requestAvailabilityNoThrottle, mPresenceSubscriber is null");
434                 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
435             }
436 
437             // check availability cache (in RAM).
438             return mSubscriber.requestAvailability(contactNumber,
439                     new CapabilityResultListener(listener), true);
440         }
441 
442         public int getPublishState() throws RemoteException {
443             return publisherPublishStateToPublishState(mPublication.getPublishState());
444         }
445     };
446 
447     @Override
onBind(Intent arg0)448     public IBinder onBind(Intent arg0) {
449         return mBinder;
450     }
451 
452     /**
453      * Receives notifications when Mobile data is enabled or disabled.
454      */
455     private class MobileDataContentObserver extends ContentObserver {
MobileDataContentObserver()456         public MobileDataContentObserver() {
457             super(new Handler());
458         }
459 
460         @Override
onChange(final boolean selfChange)461         public void onChange(final boolean selfChange) {
462             boolean enabled = Settings.Global.getInt(getContentResolver(),
463                     Settings.Global.MOBILE_DATA, 1) == 1;
464             logger.debug("Mobile data enabled status: " + (enabled ? "ON" : "OFF"));
465             onMobileDataEnabled(enabled);
466         }
467     }
468 
469     /** Observer to get notified when Mobile data enabled status changes */
470     private MobileDataContentObserver mObserver;
471 
onMobileDataEnabled(final boolean enabled)472     private void onMobileDataEnabled(final boolean enabled) {
473         logger.debug("Enter onMobileDataEnabled: " + enabled);
474         Thread thread = new Thread(new Runnable() {
475             @Override
476             public void run() {
477                 try{
478                     if(mPublication != null){
479                         mPublication.onMobileDataChanged(enabled);
480                         return;
481                     }
482                 }catch(Exception e){
483                     logger.error("Exception onMobileDataEnabled:", e);
484                 }
485             }
486         }, "onMobileDataEnabled thread");
487 
488         thread.start();
489     }
490 
491 
492     private SimInfoContentObserver mSiminfoSettingObserver;
493 
494     /**
495      * Receives notifications when the TelephonyProvider is changed.
496      */
497     private class SimInfoContentObserver extends ContentObserver {
SimInfoContentObserver()498         public SimInfoContentObserver() {
499             super(new Handler());
500         }
501 
502         @Override
onChange(final boolean selfChange)503         public void onChange(final boolean selfChange) {
504             if (mAssociatedSubscription == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
505                 return;
506             }
507             ImsMmTelManager ims = ImsMmTelManager.createForSubscriptionId(mAssociatedSubscription);
508             try {
509                 boolean enabled = ims.isVtSettingEnabled();
510                 logger.debug("vt enabled status: " + (enabled ? "ON" : "OFF"));
511                 onVtEnabled(enabled);
512             } catch (Exception e) {
513                 logger.info("Exception getting VT status for sub:" + mAssociatedSubscription
514                         + ", Exception = " + e.getMessage());
515             }
516         }
517     }
518 
onVtEnabled(boolean enabled)519     private void onVtEnabled(boolean enabled) {
520         if(mPublication != null){
521             mPublication.onVtEnabled(enabled);
522         }
523     }
524 
525     private final IRcsService.Stub mBinder = new IRcsService.Stub() {
526         /**
527          * return true if the rcs service is ready for use.
528          */
529         public boolean isRcsServiceAvailable(){
530             logger.debug("calling isRcsServiceAvailable");
531             if(mRcsStackAdaptor == null){
532                 return false;
533             }
534 
535             return mRcsStackAdaptor.isImsEnableState();
536         }
537 
538         /**
539          * Gets the presence interface.
540          *
541          * @see IRcsPresence
542          */
543         public IRcsPresence getRcsPresenceInterface(){
544             return mIRcsPresenceImpl;
545         }
546     };
547 
548     private RegistrationManager.RegistrationCallback mImsRegistrationCallback =
549             new RegistrationManager.RegistrationCallback() {
550 
551         @Override
552         public void onRegistered(int imsTransportType) {
553             logger.debug("onImsConnected imsTransportType=" + imsTransportType);
554             mNetworkRegistrationType = imsTransportType;
555             if(mPublication != null) {
556                 mPublication.onImsConnected();
557             }
558         }
559 
560         @Override
561         public void onUnregistered(ImsReasonInfo info) {
562             logger.debug("onImsDisconnected");
563             mNetworkRegistrationType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
564             if(mPublication != null) {
565                 mPublication.onImsDisconnected();
566             }
567         }
568     };
569 
570     private ImsMmTelManager.CapabilityCallback mCapabilityCallback =
571             new ImsMmTelManager.CapabilityCallback() {
572 
573         @Override
574         public void onCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities capabilities) {
575             mPublication.onFeatureCapabilityChanged(mNetworkRegistrationType, capabilities);
576         }
577     };
578 
579     private ProvisioningManager.Callback mProvisioningChangedCallback =
580             new ProvisioningManager.Callback() {
581 
582         @Override
583         public void onProvisioningIntChanged(int item, int value) {
584             switch (item) {
585                 case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
586                     // intentional fallthrough
587                 case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
588                     // intentional fallthrough
589                 case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
590                     if (mPublication != null) {
591                         mPublication.handleProvisioningChanged();
592                     }
593             }
594         }
595     };
596 
publisherPublishStateToPublishState(int publisherPublishState)597     private static int publisherPublishStateToPublishState(int publisherPublishState) {
598         switch(publisherPublishState) {
599             case PresenceBase.PUBLISH_STATE_200_OK:
600                 return RcsPresence.PublishState.PUBLISH_STATE_200_OK;
601             case PresenceBase.PUBLISH_STATE_NOT_PUBLISHED:
602                 return RcsPresence.PublishState.PUBLISH_STATE_NOT_PUBLISHED;
603             case PresenceBase.PUBLISH_STATE_VOLTE_PROVISION_ERROR:
604                 return RcsPresence.PublishState.PUBLISH_STATE_VOLTE_PROVISION_ERROR;
605             case PresenceBase.PUBLISH_STATE_RCS_PROVISION_ERROR:
606                 return RcsPresence.PublishState.PUBLISH_STATE_RCS_PROVISION_ERROR;
607             case PresenceBase.PUBLISH_STATE_REQUEST_TIMEOUT:
608                 return RcsPresence.PublishState.PUBLISH_STATE_REQUEST_TIMEOUT;
609             case PresenceBase.PUBLISH_STATE_OTHER_ERROR:
610                 return RcsPresence.PublishState.PUBLISH_STATE_OTHER_ERROR;
611 
612         }
613         return PresenceBase.PUBLISH_STATE_OTHER_ERROR;
614     }
615 }
616 
617