1 /*
2  * Copyright (C) 2017 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;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.os.IBinder;
22 import android.os.Message;
23 import android.os.RemoteException;
24 import android.telephony.TelephonyManager;
25 import android.telephony.ims.ImsCallProfile;
26 import android.telephony.ims.aidl.IImsCapabilityCallback;
27 import android.telephony.ims.aidl.IImsConfig;
28 import android.telephony.ims.aidl.IImsConfigCallback;
29 import android.telephony.ims.aidl.IImsMmTelFeature;
30 import android.telephony.ims.aidl.IImsRegistration;
31 import android.telephony.ims.aidl.IImsRegistrationCallback;
32 import android.telephony.ims.aidl.IImsSmsListener;
33 import android.telephony.ims.feature.CapabilityChangeRequest;
34 import android.telephony.ims.feature.ImsFeature;
35 import android.telephony.ims.feature.MmTelFeature;
36 import android.telephony.ims.stub.ImsSmsImplBase;
37 import android.util.Log;
38 
39 import com.android.ims.internal.IImsCallSession;
40 import com.android.ims.internal.IImsEcbm;
41 import com.android.ims.internal.IImsMultiEndpoint;
42 import com.android.ims.internal.IImsUt;
43 import com.android.telephony.Rlog;
44 
45 /**
46  * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
47  * the platform currently supports: MMTel
48  */
49 
50 public class MmTelFeatureConnection extends FeatureConnection {
51     protected static final String TAG = "MmTelFeatureConnection";
52 
53     private class ImsRegistrationCallbackAdapter extends
54             ImsCallbackAdapterManager<IImsRegistrationCallback> {
55 
ImsRegistrationCallbackAdapter(Context context, Object lock)56         public ImsRegistrationCallbackAdapter(Context context, Object lock) {
57             super(context, lock, mSlotId);
58         }
59 
60         @Override
registerCallback(IImsRegistrationCallback localCallback)61         public void registerCallback(IImsRegistrationCallback localCallback) {
62             IImsRegistration imsRegistration = getRegistration();
63             if (imsRegistration != null) {
64                 try {
65                     imsRegistration.addRegistrationCallback(localCallback);
66                 } catch (RemoteException e) {
67                     throw new IllegalStateException("ImsRegistrationCallbackAdapter: MmTelFeature"
68                             + " binder is dead.");
69                 }
70             } else {
71                 Log.e(TAG + " [" + mSlotId + "]", "ImsRegistrationCallbackAdapter: ImsRegistration"
72                         + " is null");
73                 throw new IllegalStateException("ImsRegistrationCallbackAdapter: MmTelFeature is"
74                         + "not available!");
75             }
76         }
77 
78         @Override
unregisterCallback(IImsRegistrationCallback localCallback)79         public void unregisterCallback(IImsRegistrationCallback localCallback) {
80             IImsRegistration imsRegistration = getRegistration();
81             if (imsRegistration != null) {
82                 try {
83                     imsRegistration.removeRegistrationCallback(localCallback);
84                 } catch (RemoteException e) {
85                     Log.w(TAG + " [" + mSlotId + "]", "ImsRegistrationCallbackAdapter -"
86                             + " unregisterCallback: couldn't remove registration callback");
87                 }
88             } else {
89                 Log.e(TAG + " [" + mSlotId + "]", "ImsRegistrationCallbackAdapter: ImsRegistration"
90                         + " is null");
91             }
92         }
93     }
94 
95     private class CapabilityCallbackManager extends ImsCallbackAdapterManager<IImsCapabilityCallback> {
96 
CapabilityCallbackManager(Context context, Object lock)97         public CapabilityCallbackManager(Context context, Object lock) {
98             super(context, lock, mSlotId);
99         }
100 
101         @Override
registerCallback(IImsCapabilityCallback localCallback)102         public void registerCallback(IImsCapabilityCallback localCallback) {
103             IImsMmTelFeature binder;
104             synchronized (mLock) {
105                 try {
106                     checkServiceIsReady();
107                     binder = getServiceInterface(mBinder);
108                 } catch (RemoteException e) {
109                     throw new IllegalStateException("CapabilityCallbackManager - MmTelFeature"
110                             + " binder is dead.");
111                 }
112             }
113             if (binder != null) {
114                 try {
115                 binder.addCapabilityCallback(localCallback);
116                 } catch (RemoteException e) {
117                     throw new IllegalStateException(" CapabilityCallbackManager - MmTelFeature"
118                             + " binder is null.");
119                 }
120             } else {
121                 Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, register: Couldn't"
122                         + " get binder");
123                 throw new IllegalStateException("CapabilityCallbackManager: MmTelFeature is"
124                         + " not available!");
125             }
126         }
127 
128         @Override
unregisterCallback(IImsCapabilityCallback localCallback)129         public void unregisterCallback(IImsCapabilityCallback localCallback) {
130             IImsMmTelFeature binder;
131             synchronized (mLock) {
132                 try {
133                     checkServiceIsReady();
134                     binder = getServiceInterface(mBinder);
135                 } catch (RemoteException e) {
136                     // binder is null
137                     Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, unregister:"
138                             + " couldn't get binder.");
139                     return;
140                 }
141             }
142             if (binder != null) {
143                 try {
144                     binder.removeCapabilityCallback(localCallback);
145                 } catch (RemoteException e) {
146                     Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, unregister:"
147                             + " Binder is dead.");
148                 }
149             } else {
150                 Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, unregister:"
151                         + " binder is null.");
152             }
153         }
154     }
155 
156     private class ProvisioningCallbackManager extends ImsCallbackAdapterManager<IImsConfigCallback> {
ProvisioningCallbackManager(Context context, Object lock)157         public ProvisioningCallbackManager (Context context, Object lock) {
158             super(context, lock, mSlotId);
159         }
160 
161         @Override
registerCallback(IImsConfigCallback localCallback)162         public void registerCallback(IImsConfigCallback localCallback) {
163             IImsConfig binder = getConfigInterface();
164             if (binder == null) {
165                 // Config interface is not currently available.
166                 Log.w(TAG + " [" + mSlotId + "]", "ProvisioningCallbackManager - couldn't register,"
167                         + " binder is null.");
168                 throw new IllegalStateException("ImsConfig is not available!");
169             }
170             try {
171                 binder.addImsConfigCallback(localCallback);
172             }catch (RemoteException e) {
173                 throw new IllegalStateException("ImsService is not available!");
174             }
175         }
176 
177         @Override
unregisterCallback(IImsConfigCallback localCallback)178         public void unregisterCallback(IImsConfigCallback localCallback) {
179             IImsConfig binder = getConfigInterface();
180             if (binder == null) {
181                 Log.w(TAG + " [" + mSlotId + "]", "ProvisioningCallbackManager - couldn't"
182                         + " unregister, binder is null.");
183                 return;
184             }
185             try {
186                 binder.removeImsConfigCallback(localCallback);
187             } catch (RemoteException e) {
188                 Log.w(TAG + " [" + mSlotId + "]", "ProvisioningCallbackManager - couldn't"
189                         + " unregister, binder is dead.");
190             }
191         }
192     }
193 
194     // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent.
195     private boolean mSupportsEmergencyCalling = false;
196 
197     // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If
198     // it becomes disconnected, invalidate.
199     private IImsConfig mConfigBinder;
200     private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager;
201     private final CapabilityCallbackManager mCapabilityCallbackManager;
202     private final ProvisioningCallbackManager mProvisioningCallbackManager;
203 
create(Context context , int slotId)204     public static @NonNull MmTelFeatureConnection create(Context context , int slotId) {
205         MmTelFeatureConnection serviceProxy = new MmTelFeatureConnection(context, slotId);
206         if (!ImsManager.isImsSupportedOnDevice(context)) {
207             // Return empty service proxy in the case that IMS is not supported.
208             sImsSupportedOnDevice = false;
209             return serviceProxy;
210         }
211 
212         TelephonyManager tm = serviceProxy.getTelephonyManager();
213         if (tm == null) {
214             Rlog.w(TAG + " [" + slotId + "]", "create: TelephonyManager is null!");
215             // Binder can be unset in this case because it will be torn down/recreated as part of
216             // a retry mechanism until the serviceProxy binder is set successfully.
217             return serviceProxy;
218         }
219 
220         IImsMmTelFeature binder = tm.getImsMmTelFeatureAndListen(slotId,
221                 serviceProxy.getListener());
222         if (binder != null) {
223             serviceProxy.setBinder(binder.asBinder());
224             // Trigger the cache to be updated for feature status.
225             serviceProxy.getFeatureState();
226         } else {
227             Rlog.w(TAG + " [" + slotId + "]", "create: binder is null!");
228         }
229         return serviceProxy;
230     }
231 
MmTelFeatureConnection(Context context, int slotId)232     public MmTelFeatureConnection(Context context, int slotId) {
233         super(context, slotId);
234 
235         mRegistrationCallbackManager = new ImsRegistrationCallbackAdapter(context, mLock);
236         mCapabilityCallbackManager = new CapabilityCallbackManager(context, mLock);
237         mProvisioningCallbackManager = new ProvisioningCallbackManager(context, mLock);
238     }
239 
240     @Override
onRemovedOrDied()241     protected void onRemovedOrDied() {
242         removeImsFeatureCallback();
243         synchronized (mLock) {
244             super.onRemovedOrDied();
245             mRegistrationCallbackManager.close();
246             mCapabilityCallbackManager.close();
247             mProvisioningCallbackManager.close();
248             mConfigBinder = null;
249         }
250     }
251 
removeImsFeatureCallback()252     private void removeImsFeatureCallback() {
253         TelephonyManager tm = getTelephonyManager();
254         if (tm != null) {
255             tm.unregisterImsFeatureCallback(mSlotId, ImsFeature.FEATURE_MMTEL, getListener());
256         }
257     }
258 
getConfig()259     private IImsConfig getConfig() {
260         synchronized (mLock) {
261             // null if cache is invalid;
262             if (mConfigBinder != null) {
263                 return mConfigBinder;
264             }
265         }
266         TelephonyManager tm = getTelephonyManager();
267         IImsConfig configBinder = tm != null
268                 ? tm.getImsConfig(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
269         synchronized (mLock) {
270             // mConfigBinder may have changed while we tried to get the config interface.
271             if (mConfigBinder == null) {
272                 mConfigBinder = configBinder;
273             }
274         }
275         return mConfigBinder;
276     }
277 
278     @Override
handleImsFeatureCreatedCallback(int slotId, int feature)279     protected void handleImsFeatureCreatedCallback(int slotId, int feature) {
280         // The feature has been enabled. This happens when the feature is first created and
281         // may happen when the feature is re-enabled.
282         synchronized (mLock) {
283             if(mSlotId != slotId) {
284                 return;
285             }
286             switch (feature) {
287                 case ImsFeature.FEATURE_MMTEL: {
288                     if (!mIsAvailable) {
289                         Log.i(TAG + " [" + mSlotId + "]", "MmTel enabled");
290                         mIsAvailable = true;
291                     }
292                     break;
293                 }
294                 case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
295                     mSupportsEmergencyCalling = true;
296                     Log.i(TAG + " [" + mSlotId + "]", "Emergency calling enabled");
297                     break;
298                 }
299             }
300         }
301     }
302 
303     @Override
handleImsFeatureRemovedCallback(int slotId, int feature)304     protected void handleImsFeatureRemovedCallback(int slotId, int feature) {
305         synchronized (mLock) {
306             if (mSlotId != slotId) {
307                 return;
308             }
309             switch (feature) {
310                 case ImsFeature.FEATURE_MMTEL: {
311                     Log.i(TAG + " [" + mSlotId + "]", "MmTel removed");
312                     onRemovedOrDied();
313                     break;
314                 }
315                 case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
316                     mSupportsEmergencyCalling = false;
317                     Log.i(TAG + " [" + mSlotId + "]", "Emergency calling disabled");
318                     break;
319                 }
320             }
321         }
322     }
323 
324     @Override
handleImsStatusChangedCallback(int slotId, int feature, int status)325     protected void handleImsStatusChangedCallback(int slotId, int feature, int status) {
326         synchronized (mLock) {
327             Log.i(TAG + " [" + mSlotId + "]", "imsStatusChanged: slot: " + slotId + " feature: "
328                 + ImsFeature.FEATURE_LOG_MAP.get(feature) +
329                 " status: " + ImsFeature.STATE_LOG_MAP.get(status));
330             if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
331                 mFeatureStateCached = status;
332                 if (mStatusCallback != null) {
333                     mStatusCallback.notifyStateChanged();
334                 }
335             }
336         }
337     }
338 
isEmergencyMmTelAvailable()339     public boolean isEmergencyMmTelAvailable() {
340         return mSupportsEmergencyCalling;
341     }
342 
343     /**
344      * Opens the connection to the {@link MmTelFeature} and establishes a listener back to the
345      * framework. Calling this method multiple times will reset the listener attached to the
346      * {@link MmTelFeature}.
347      * @param listener A {@link MmTelFeature.Listener} that will be used by the {@link MmTelFeature}
348      * to notify the framework of updates.
349      */
openConnection(MmTelFeature.Listener listener)350     public void openConnection(MmTelFeature.Listener listener) throws RemoteException {
351         synchronized (mLock) {
352             checkServiceIsReady();
353             getServiceInterface(mBinder).setListener(listener);
354         }
355     }
356 
closeConnection()357     public void closeConnection() {
358         mRegistrationCallbackManager.close();
359         mCapabilityCallbackManager.close();
360         mProvisioningCallbackManager.close();
361         try {
362             synchronized (mLock) {
363                 if (isBinderAlive()) {
364                     getServiceInterface(mBinder).setListener(null);
365                 }
366             }
367         } catch (RemoteException e) {
368             Log.w(TAG + " [" + mSlotId + "]", "closeConnection: couldn't remove listener!");
369         }
370     }
371 
addRegistrationCallback(IImsRegistrationCallback callback)372     public void addRegistrationCallback(IImsRegistrationCallback callback) {
373         mRegistrationCallbackManager.addCallback(callback);
374     }
375 
addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)376     public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
377             int subId) {
378         mRegistrationCallbackManager.addCallbackForSubscription(callback , subId);
379     }
380 
removeRegistrationCallback(IImsRegistrationCallback callback)381     public void removeRegistrationCallback(IImsRegistrationCallback callback) {
382         mRegistrationCallbackManager.removeCallback(callback);
383     }
384 
removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)385     public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
386             int subId) {
387         mRegistrationCallbackManager.removeCallbackForSubscription(callback, subId);
388     }
389 
addCapabilityCallback(IImsCapabilityCallback callback)390     public void addCapabilityCallback(IImsCapabilityCallback callback) {
391         mCapabilityCallbackManager.addCallback(callback);
392     }
393 
addCapabilityCallbackForSubscription(IImsCapabilityCallback callback, int subId)394     public void addCapabilityCallbackForSubscription(IImsCapabilityCallback callback,
395             int subId) {
396         mCapabilityCallbackManager.addCallbackForSubscription(callback, subId);
397     }
398 
removeCapabilityCallback(IImsCapabilityCallback callback)399     public void removeCapabilityCallback(IImsCapabilityCallback callback) {
400         mCapabilityCallbackManager.removeCallback(callback);
401     }
402 
removeCapabilityCallbackForSubscription(IImsCapabilityCallback callback, int subId)403     public void removeCapabilityCallbackForSubscription(IImsCapabilityCallback callback,
404             int subId) {
405         mCapabilityCallbackManager.removeCallbackForSubscription(callback , subId);
406     }
407 
addProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)408     public void addProvisioningCallbackForSubscription(IImsConfigCallback callback,
409             int subId) {
410         mProvisioningCallbackManager.addCallbackForSubscription(callback, subId);
411     }
412 
removeProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)413     public void removeProvisioningCallbackForSubscription(IImsConfigCallback callback,
414             int subId) {
415         mProvisioningCallbackManager.removeCallbackForSubscription(callback , subId);
416     }
417 
changeEnabledCapabilities(CapabilityChangeRequest request, IImsCapabilityCallback callback)418     public void changeEnabledCapabilities(CapabilityChangeRequest request,
419             IImsCapabilityCallback callback) throws RemoteException {
420         synchronized (mLock) {
421             checkServiceIsReady();
422             getServiceInterface(mBinder).changeCapabilitiesConfiguration(request, callback);
423         }
424     }
425 
queryEnabledCapabilities(int capability, int radioTech, IImsCapabilityCallback callback)426     public void queryEnabledCapabilities(int capability, int radioTech,
427             IImsCapabilityCallback callback) throws RemoteException {
428         synchronized (mLock) {
429             checkServiceIsReady();
430             getServiceInterface(mBinder).queryCapabilityConfiguration(capability, radioTech,
431                     callback);
432         }
433     }
434 
queryCapabilityStatus()435     public MmTelFeature.MmTelCapabilities queryCapabilityStatus() throws RemoteException {
436         synchronized (mLock) {
437             checkServiceIsReady();
438             return new MmTelFeature.MmTelCapabilities(
439                     getServiceInterface(mBinder).queryCapabilityStatus());
440         }
441     }
442 
createCallProfile(int callServiceType, int callType)443     public ImsCallProfile createCallProfile(int callServiceType, int callType)
444             throws RemoteException {
445         synchronized (mLock) {
446             checkServiceIsReady();
447             return getServiceInterface(mBinder).createCallProfile(callServiceType, callType);
448         }
449     }
450 
createCallSession(ImsCallProfile profile)451     public IImsCallSession createCallSession(ImsCallProfile profile)
452             throws RemoteException {
453         synchronized (mLock) {
454             checkServiceIsReady();
455             return getServiceInterface(mBinder).createCallSession(profile);
456         }
457     }
458 
getUtInterface()459     public IImsUt getUtInterface() throws RemoteException {
460         synchronized (mLock) {
461             checkServiceIsReady();
462             return getServiceInterface(mBinder).getUtInterface();
463         }
464     }
465 
getConfigInterface()466     public IImsConfig getConfigInterface() {
467         return getConfig();
468     }
469 
getEcbmInterface()470     public IImsEcbm getEcbmInterface() throws RemoteException {
471         synchronized (mLock) {
472             checkServiceIsReady();
473             return getServiceInterface(mBinder).getEcbmInterface();
474         }
475     }
476 
setUiTTYMode(int uiTtyMode, Message onComplete)477     public void setUiTTYMode(int uiTtyMode, Message onComplete)
478             throws RemoteException {
479         synchronized (mLock) {
480             checkServiceIsReady();
481             getServiceInterface(mBinder).setUiTtyMode(uiTtyMode, onComplete);
482         }
483     }
484 
getMultiEndpointInterface()485     public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
486         synchronized (mLock) {
487             checkServiceIsReady();
488             return getServiceInterface(mBinder).getMultiEndpointInterface();
489         }
490     }
491 
sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)492     public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
493             byte[] pdu) throws RemoteException {
494         synchronized (mLock) {
495             checkServiceIsReady();
496             getServiceInterface(mBinder).sendSms(token, messageRef, format, smsc, isRetry,
497                     pdu);
498         }
499     }
500 
acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.SendStatusResult int result)501     public void acknowledgeSms(int token, int messageRef,
502             @ImsSmsImplBase.SendStatusResult int result) throws RemoteException {
503         synchronized (mLock) {
504             checkServiceIsReady();
505             getServiceInterface(mBinder).acknowledgeSms(token, messageRef, result);
506         }
507     }
508 
acknowledgeSmsReport(int token, int messageRef, @ImsSmsImplBase.StatusReportResult int result)509     public void acknowledgeSmsReport(int token, int messageRef,
510             @ImsSmsImplBase.StatusReportResult int result) throws RemoteException {
511         synchronized (mLock) {
512             checkServiceIsReady();
513             getServiceInterface(mBinder).acknowledgeSmsReport(token, messageRef, result);
514         }
515     }
516 
getSmsFormat()517     public String getSmsFormat() throws RemoteException {
518         synchronized (mLock) {
519             checkServiceIsReady();
520             return getServiceInterface(mBinder).getSmsFormat();
521         }
522     }
523 
onSmsReady()524     public void onSmsReady() throws RemoteException {
525         synchronized (mLock) {
526             checkServiceIsReady();
527             getServiceInterface(mBinder).onSmsReady();
528         }
529     }
530 
setSmsListener(IImsSmsListener listener)531     public void setSmsListener(IImsSmsListener listener) throws RemoteException {
532         synchronized (mLock) {
533             checkServiceIsReady();
534             getServiceInterface(mBinder).setSmsListener(listener);
535         }
536     }
537 
shouldProcessCall(boolean isEmergency, String[] numbers)538     public @MmTelFeature.ProcessCallResult int shouldProcessCall(boolean isEmergency,
539             String[] numbers) throws RemoteException {
540         if (isEmergency && !isEmergencyMmTelAvailable()) {
541             // Don't query the ImsService if emergency calling is not available on the ImsService.
542             Log.i(TAG + " [" + mSlotId + "]", "MmTel does not support emergency over IMS, fallback"
543                     + " to CS.");
544             return MmTelFeature.PROCESS_CALL_CSFB;
545         }
546         synchronized (mLock) {
547             checkServiceIsReady();
548             return getServiceInterface(mBinder).shouldProcessCall(numbers);
549         }
550     }
551 
552     @Override
retrieveFeatureState()553     protected Integer retrieveFeatureState() {
554         if (mBinder != null) {
555             try {
556                 return getServiceInterface(mBinder).getFeatureState();
557             } catch (RemoteException e) {
558                 // Status check failed, don't update cache
559             }
560         }
561         return null;
562     }
563 
564     @Override
getRegistrationBinder()565     protected IImsRegistration getRegistrationBinder() {
566         TelephonyManager tm = getTelephonyManager();
567         return  tm != null ? tm.getImsRegistration(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
568     }
569 
getServiceInterface(IBinder b)570     private IImsMmTelFeature getServiceInterface(IBinder b) {
571         return IImsMmTelFeature.Stub.asInterface(b);
572     }
573 }
574