blob: 6b140d939601e8480ec617134b06073570184181 [file] [log] [blame]
Brad Ebinger936a7d12018-01-16 09:36:56 -08001/*
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
17package com.android.ims;
18
19import android.annotation.Nullable;
20import android.content.Context;
21import android.net.Uri;
22import android.os.IBinder;
23import android.os.Message;
24import android.os.RemoteException;
25import android.telephony.Rlog;
26import android.telephony.TelephonyManager;
Brad Ebingered690772018-01-23 13:41:32 -080027import android.telephony.ims.ImsCallProfile;
28import android.telephony.ims.ImsReasonInfo;
Brad Ebinger936a7d12018-01-16 09:36:56 -080029import android.telephony.ims.aidl.IImsConfig;
30import android.telephony.ims.aidl.IImsMmTelFeature;
31import android.telephony.ims.aidl.IImsRegistration;
32import android.telephony.ims.aidl.IImsRegistrationCallback;
33import android.telephony.ims.aidl.IImsSmsListener;
34import android.telephony.ims.feature.CapabilityChangeRequest;
35import android.telephony.ims.feature.ImsFeature;
36import android.telephony.ims.feature.MmTelFeature;
37import android.telephony.ims.stub.ImsRegistrationImplBase;
38import android.telephony.ims.stub.ImsSmsImplBase;
39import android.util.Log;
40
41import com.android.ims.internal.IImsCallSession;
42import com.android.ims.internal.IImsEcbm;
43import com.android.ims.internal.IImsMultiEndpoint;
44import com.android.ims.internal.IImsServiceFeatureCallback;
45import com.android.ims.internal.IImsUt;
46
47import java.util.HashSet;
48import java.util.Set;
49
50/**
51 * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
52 * the platform currently supports: MMTel and RCS.
53 * @hide
54 */
55
56public class MmTelFeatureConnection {
57
58 protected static final String TAG = "MmTelFeatureConnection";
59 protected final int mSlotId;
60 protected IBinder mBinder;
61 private Context mContext;
62
Brad Ebingerfe2b2222018-03-08 16:16:15 -080063 private volatile boolean mIsAvailable = false;
Brad Ebinger936a7d12018-01-16 09:36:56 -080064 // ImsFeature Status from the ImsService. Cached.
65 private Integer mFeatureStateCached = null;
66 private IFeatureUpdate mStatusCallback;
67 private final Object mLock = new Object();
Brad Ebingerfe2b2222018-03-08 16:16:15 -080068 // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent.
69 private boolean mSupportsEmergencyCalling = false;
Brad Ebinger936a7d12018-01-16 09:36:56 -080070
Brad Ebinger48632582018-03-16 11:46:30 -070071 // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If
72 // it becomes disconnected, invalidate.
73 private IImsRegistration mRegistrationBinder;
74 private IImsConfig mConfigBinder;
Brad Ebinger936a7d12018-01-16 09:36:56 -080075
76 private abstract class CallbackAdapterManager<T> {
77 private static final String TAG = "CallbackAdapterManager";
78
79 protected final Set<T> mLocalCallbacks = new HashSet<>();
80 private boolean mHasConnected = false;
81
82 public void addCallback(T localCallback) throws RemoteException {
83 // We only one one binding to the ImsService per process.
84 // Store any more locally.
85 synchronized (mLock) {
86 if(!mHasConnected) {
87 // throws a RemoteException if a connection can not be established.
88 if(createConnection()) {
89 mHasConnected = true;
90 } else {
91 throw new RemoteException("Can not create connection!");
92 }
93 }
94 Log.i(TAG, "Local callback added: " + localCallback);
95 mLocalCallbacks.add(localCallback);
96 }
97 }
98
99 public void removeCallback(T localCallback) {
100 // We only maintain one binding to the ImsService per process.
101 synchronized (mLock) {
102 Log.i(TAG, "Local callback removed: " + localCallback);
103 mLocalCallbacks.remove(localCallback);
104 // If we have removed all local callbacks, remove callback to ImsService.
105 if(mHasConnected) {
106 if (mLocalCallbacks.isEmpty()) {
107 removeConnection();
108 mHasConnected = false;
109 }
110 }
111 }
112 }
113
114 public void close() {
115 synchronized (mLock) {
116 if (mHasConnected) {
117 removeConnection();
118 // Still mark the connection as disconnected, even if this fails.
119 mHasConnected = false;
120 }
121 Log.i(TAG, "Closing connection and clearing callbacks");
122 mLocalCallbacks.clear();
123 }
124 }
125
126 abstract boolean createConnection() throws RemoteException;
127
128 abstract void removeConnection();
129 }
130 private ImsRegistrationCallbackAdapter mRegistrationCallbackManager
131 = new ImsRegistrationCallbackAdapter();
132 private class ImsRegistrationCallbackAdapter
133 extends CallbackAdapterManager<ImsRegistrationImplBase.Callback> {
134 private final RegistrationCallbackAdapter mRegistrationCallbackAdapter
135 = new RegistrationCallbackAdapter();
136
137 private class RegistrationCallbackAdapter extends IImsRegistrationCallback.Stub {
138
139 @Override
140 public void onRegistered(int imsRadioTech) {
141 Log.i(TAG, "onRegistered ::");
142
143 synchronized (mLock) {
144 mLocalCallbacks.forEach(l -> l.onRegistered(imsRadioTech));
145 }
146 }
147
148 @Override
149 public void onRegistering(int imsRadioTech) {
150 Log.i(TAG, "onRegistering ::");
151
152 synchronized (mLock) {
153 mLocalCallbacks.forEach(l -> l.onRegistering(imsRadioTech));
154 }
155 }
156
157 @Override
158 public void onDeregistered(ImsReasonInfo imsReasonInfo) {
159 Log.i(TAG, "onDeregistered ::");
160
161 synchronized (mLock) {
162 mLocalCallbacks.forEach(l -> l.onDeregistered(imsReasonInfo));
163 }
164 }
165
166 @Override
167 public void onTechnologyChangeFailed(int targetRadioTech, ImsReasonInfo imsReasonInfo) {
168 Log.i(TAG, "onTechnologyChangeFailed :: targetAccessTech=" + targetRadioTech +
169 ", imsReasonInfo=" + imsReasonInfo);
170
171 synchronized (mLock) {
172 mLocalCallbacks.forEach(l -> l.onTechnologyChangeFailed(targetRadioTech,
173 imsReasonInfo));
174 }
175 }
176
177 @Override
178 public void onSubscriberAssociatedUriChanged(Uri[] uris) {
179 Log.i(TAG, "onSubscriberAssociatedUriChanged");
180 synchronized (mLock) {
181 mLocalCallbacks.forEach(l -> l.onSubscriberAssociatedUriChanged(uris));
182 }
183 }
184 }
185
186 @Override
187 boolean createConnection() throws RemoteException {
188 IImsRegistration imsRegistration = getRegistration();
189 if (imsRegistration != null) {
190 getRegistration().addRegistrationCallback(mRegistrationCallbackAdapter);
191 return true;
192 } else {
193 Log.e(TAG, "ImsRegistration is null");
194 return false;
195 }
196 }
197
198 @Override
199 void removeConnection() {
200 IImsRegistration imsRegistration = getRegistration();
201 if (imsRegistration != null) {
202 try {
Brad Ebingerd1b1a3c2018-03-08 11:37:35 -0800203 getRegistration().removeRegistrationCallback(mRegistrationCallbackAdapter);
Brad Ebinger936a7d12018-01-16 09:36:56 -0800204 } catch (RemoteException e) {
205 Log.w(TAG, "removeConnection: couldn't remove registration callback");
206 }
207 } else {
208 Log.e(TAG, "ImsRegistration is null");
209 }
210 }
211 }
212
213 private final CapabilityCallbackManager mCapabilityCallbackManager
214 = new CapabilityCallbackManager();
215 private class CapabilityCallbackManager
216 extends CallbackAdapterManager<ImsFeature.CapabilityCallback> {
217 private final CapabilityCallbackAdapter mCallbackAdapter = new CapabilityCallbackAdapter();
218
219 private class CapabilityCallbackAdapter extends ImsFeature.CapabilityCallback {
220 // Called when the Capabilities Status on this connection have changed.
221 @Override
222 public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
223 synchronized (mLock) {
224 mLocalCallbacks.forEach(
225 callback -> callback.onCapabilitiesStatusChanged(config));
226 }
227 }
228 }
229
230 @Override
231 boolean createConnection() throws RemoteException {
232 IImsMmTelFeature binder = getServiceInterface(mBinder);
233 if (binder != null) {
234 binder.addCapabilityCallback(mCallbackAdapter);
235 return true;
236 } else {
237 Log.w(TAG, "create: Couldn't get IImsMmTelFeature binder");
238 return false;
239 }
240 }
241
242 @Override
243 void removeConnection() {
244 IImsMmTelFeature binder = getServiceInterface(mBinder);
245 if (binder != null) {
246 try {
247 binder.removeCapabilityCallback(mCallbackAdapter);
248 } catch (RemoteException e) {
249 Log.w(TAG, "remove: IImsMmTelFeature binder is dead");
250 }
251 } else {
252 Log.w(TAG, "remove: Couldn't get IImsMmTelFeature binder");
253 }
254 }
255 }
256
257
258 public static MmTelFeatureConnection create(Context context , int slotId) {
259 MmTelFeatureConnection serviceProxy = new MmTelFeatureConnection(context, slotId);
260
261 TelephonyManager tm = getTelephonyManager(context);
262 if (tm == null) {
263 Rlog.w(TAG, "create: TelephonyManager is null!");
264 // Binder can be unset in this case because it will be torn down/recreated as part of
265 // a retry mechanism until the serviceProxy binder is set successfully.
266 return serviceProxy;
267 }
268
269 IImsMmTelFeature binder = tm.getImsMmTelFeatureAndListen(slotId,
270 serviceProxy.getListener());
271 if (binder != null) {
272 serviceProxy.setBinder(binder.asBinder());
273 // Trigger the cache to be updated for feature status.
274 serviceProxy.getFeatureState();
275 } else {
276 Rlog.w(TAG, "create: binder is null! Slot Id: " + slotId);
277 }
278 return serviceProxy;
279 }
280
281 public static TelephonyManager getTelephonyManager(Context context) {
282 return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
283 }
284
285 public interface IFeatureUpdate {
286 /**
287 * Called when the ImsFeature has changed its state. Use
288 * {@link ImsFeature#getFeatureState()} to get the new state.
289 */
290 void notifyStateChanged();
291
292 /**
293 * Called when the ImsFeature has become unavailable due to the binder switching or app
294 * crashing. A new ImsServiceProxy should be requested for that feature.
295 */
296 void notifyUnavailable();
297 }
298
299 private final IImsServiceFeatureCallback mListenerBinder =
300 new IImsServiceFeatureCallback.Stub() {
301
302 @Override
303 public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800304 // The feature has been enabled. This happens when the feature is first created and may
305 // happen when the feature is re-enabled.
Brad Ebinger936a7d12018-01-16 09:36:56 -0800306 synchronized (mLock) {
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800307 if(mSlotId != slotId) {
308 return;
Brad Ebinger936a7d12018-01-16 09:36:56 -0800309 }
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800310 switch (feature) {
311 case ImsFeature.FEATURE_MMTEL: {
312 if (!mIsAvailable) {
313 Log.i(TAG, "MmTel enabled on slotId: " + slotId);
314 mIsAvailable = true;
315 }
316 break;
317 }
318 case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
319 mSupportsEmergencyCalling = true;
320 Log.i(TAG, "Emergency calling enabled on slotId: " + slotId);
321 break;
322 }
323 }
324
Brad Ebinger936a7d12018-01-16 09:36:56 -0800325 }
326 }
327
328 @Override
329 public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
330 synchronized (mLock) {
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800331 if(mSlotId != slotId) {
332 return;
333 }
334 switch (feature) {
335 case ImsFeature.FEATURE_MMTEL: {
336 if (mIsAvailable) {
337 Log.i(TAG, "MmTel disabled on slotId: " + slotId);
338 mIsAvailable = false;
Brad Ebinger48632582018-03-16 11:46:30 -0700339 mmTelFeatureRemoved();
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800340 if (mStatusCallback != null) {
341 mStatusCallback.notifyUnavailable();
342 }
343 }
344 break;
345 }
346 case ImsFeature.FEATURE_EMERGENCY_MMTEL : {
347 mSupportsEmergencyCalling = false;
348 Log.i(TAG, "Emergency calling disabled on slotId: " + slotId);
349 break;
Brad Ebinger936a7d12018-01-16 09:36:56 -0800350 }
351 }
352 }
353 }
354
355 @Override
356 public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
357 synchronized (mLock) {
358 Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
359 " status: " + status);
360 if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
361 mFeatureStateCached = status;
362 if (mStatusCallback != null) {
363 mStatusCallback.notifyStateChanged();
364 }
365 }
366 }
367 }
368 };
369
370 public MmTelFeatureConnection(Context context, int slotId) {
371 mSlotId = slotId;
372 mContext = context;
373 }
374
Brad Ebinger48632582018-03-16 11:46:30 -0700375 // Called when the MmTelFeatureConnection has received an unavailable notification.
376 private void mmTelFeatureRemoved() {
377 synchronized (mLock) {
378 // invalidate caches.
379 mRegistrationBinder = null;
380 mConfigBinder = null;
381 }
382 }
383
Brad Ebinger936a7d12018-01-16 09:36:56 -0800384 private @Nullable IImsRegistration getRegistration() {
Brad Ebinger48632582018-03-16 11:46:30 -0700385 synchronized (mLock) {
386 // null if cache is invalid;
387 if (mRegistrationBinder != null) {
388 return mRegistrationBinder;
389 }
390 }
Brad Ebinger936a7d12018-01-16 09:36:56 -0800391 TelephonyManager tm = getTelephonyManager(mContext);
Brad Ebinger48632582018-03-16 11:46:30 -0700392 // We don't want to synchronize on a binder call to another process.
393 IImsRegistration regBinder = tm != null
394 ? tm.getImsRegistration(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
395 synchronized (mLock) {
396 // mRegistrationBinder may have changed while we tried to get the registration
397 // interface.
398 if (mRegistrationBinder == null) {
399 mRegistrationBinder = regBinder;
400 }
401 }
402 return mRegistrationBinder;
Brad Ebinger936a7d12018-01-16 09:36:56 -0800403 }
404
405 private IImsConfig getConfig() {
Brad Ebinger48632582018-03-16 11:46:30 -0700406 synchronized (mLock) {
407 // null if cache is invalid;
408 if (mConfigBinder != null) {
409 return mConfigBinder;
410 }
411 }
Brad Ebinger936a7d12018-01-16 09:36:56 -0800412 TelephonyManager tm = getTelephonyManager(mContext);
Brad Ebinger48632582018-03-16 11:46:30 -0700413 IImsConfig configBinder = tm != null
414 ? tm.getImsConfig(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
415 synchronized (mLock) {
416 // mConfigBinder may have changed while we tried to get the config interface.
417 if (mConfigBinder == null) {
418 mConfigBinder = configBinder;
419 }
420 }
421 return mConfigBinder;
Brad Ebinger936a7d12018-01-16 09:36:56 -0800422 }
423
Brad Ebingerd449b232018-02-12 14:33:05 -0800424 public boolean isEmergencyMmTelAvailable() {
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800425 return mSupportsEmergencyCalling;
Brad Ebingerd449b232018-02-12 14:33:05 -0800426 }
427
Brad Ebinger936a7d12018-01-16 09:36:56 -0800428 public IImsServiceFeatureCallback getListener() {
429 return mListenerBinder;
430 }
431
432 public void setBinder(IBinder binder) {
433 mBinder = binder;
434 }
435
436 /**
437 * Opens the connection to the {@link MmTelFeature} and establishes a listener back to the
438 * framework. Calling this method multiple times will reset the listener attached to the
439 * {@link MmTelFeature}.
440 * @param listener A {@link MmTelFeature.Listener} that will be used by the {@link MmTelFeature}
441 * to notify the framework of updates.
442 */
443 public void openConnection(MmTelFeature.Listener listener) throws RemoteException {
444 synchronized (mLock) {
445 checkServiceIsReady();
Brad Ebinger48632582018-03-16 11:46:30 -0700446 getServiceInterface(mBinder).setListener(listener);
Brad Ebinger936a7d12018-01-16 09:36:56 -0800447 }
448 }
449
450 public void closeConnection() {
451 mRegistrationCallbackManager.close();
452 mCapabilityCallbackManager.close();
453 try {
454 getServiceInterface(mBinder).setListener(null);
455 } catch (RemoteException e) {
456 Log.w(TAG, "closeConnection: couldn't remove listener!");
457 }
458 }
459
460 public void addRegistrationCallback(ImsRegistrationImplBase.Callback callback)
461 throws RemoteException {
462 mRegistrationCallbackManager.addCallback(callback);
463 }
464
465 public void removeRegistrationCallback(ImsRegistrationImplBase.Callback callback)
466 throws RemoteException {
467 mRegistrationCallbackManager.removeCallback(callback);
468 }
469
470 public void addCapabilityCallback(ImsFeature.CapabilityCallback callback)
471 throws RemoteException {
472 mCapabilityCallbackManager.addCallback(callback);
473 }
474
475 public void removeCapabilityCallback(ImsFeature.CapabilityCallback callback)
476 throws RemoteException {
477 mCapabilityCallbackManager.removeCallback(callback);
478 }
479
480 public void changeEnabledCapabilities(CapabilityChangeRequest request,
481 ImsFeature.CapabilityCallback callback) throws RemoteException {
482 synchronized (mLock) {
483 checkServiceIsReady();
484 getServiceInterface(mBinder).changeCapabilitiesConfiguration(request, callback);
485 }
486 }
487
488 public void queryEnabledCapabilities(int capability, int radioTech,
489 ImsFeature.CapabilityCallback callback) throws RemoteException {
490 synchronized (mLock) {
491 checkServiceIsReady();
492 getServiceInterface(mBinder).queryCapabilityConfiguration(capability, radioTech,
493 callback);
494 }
495 }
496
497 public MmTelFeature.MmTelCapabilities queryCapabilityStatus() throws RemoteException {
498 synchronized (mLock) {
499 checkServiceIsReady();
500 return new MmTelFeature.MmTelCapabilities(
501 getServiceInterface(mBinder).queryCapabilityStatus());
502 }
503 }
504
505 public ImsCallProfile createCallProfile(int callServiceType, int callType)
506 throws RemoteException {
507 synchronized (mLock) {
508 checkServiceIsReady();
509 return getServiceInterface(mBinder).createCallProfile(callServiceType, callType);
510 }
511 }
512
513 public IImsCallSession createCallSession(ImsCallProfile profile)
514 throws RemoteException {
515 synchronized (mLock) {
516 checkServiceIsReady();
517 return getServiceInterface(mBinder).createCallSession(profile);
518 }
519 }
520
521 public IImsUt getUtInterface() throws RemoteException {
522 synchronized (mLock) {
523 checkServiceIsReady();
524 return getServiceInterface(mBinder).getUtInterface();
525 }
526 }
527
528 public IImsConfig getConfigInterface() throws RemoteException {
529 synchronized (mLock) {
530 checkServiceIsReady();
531 return getConfig();
532 }
533 }
534
535 public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech()
536 throws RemoteException {
537 synchronized (mLock) {
538 checkServiceIsReady();
539 IImsRegistration registration = getRegistration();
540 if (registration != null) {
541 return registration.getRegistrationTechnology();
542 } else {
543 return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
544 }
545 }
546 }
547
548 public IImsEcbm getEcbmInterface() throws RemoteException {
549 synchronized (mLock) {
550 checkServiceIsReady();
551 return getServiceInterface(mBinder).getEcbmInterface();
552 }
553 }
554
555 public void setUiTTYMode(int uiTtyMode, Message onComplete)
556 throws RemoteException {
557 synchronized (mLock) {
558 checkServiceIsReady();
559 getServiceInterface(mBinder).setUiTtyMode(uiTtyMode, onComplete);
560 }
561 }
562
563 public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
564 synchronized (mLock) {
565 checkServiceIsReady();
566 return getServiceInterface(mBinder).getMultiEndpointInterface();
567 }
568 }
569
570 public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
571 byte[] pdu) throws RemoteException {
572 synchronized (mLock) {
573 checkServiceIsReady();
574 getServiceInterface(mBinder).sendSms(token, messageRef, format, smsc, isRetry,
575 pdu);
576 }
577 }
578
579 public void acknowledgeSms(int token, int messageRef,
580 @ImsSmsImplBase.SendStatusResult int result) throws RemoteException {
581 synchronized (mLock) {
582 checkServiceIsReady();
583 getServiceInterface(mBinder).acknowledgeSms(token, messageRef, result);
584 }
585 }
586
587 public void acknowledgeSmsReport(int token, int messageRef,
588 @ImsSmsImplBase.StatusReportResult int result) throws RemoteException {
589 synchronized (mLock) {
590 checkServiceIsReady();
591 getServiceInterface(mBinder).acknowledgeSmsReport(token, messageRef, result);
592 }
593 }
594
595 public String getSmsFormat() throws RemoteException {
596 synchronized (mLock) {
597 checkServiceIsReady();
598 return getServiceInterface(mBinder).getSmsFormat();
599 }
600 }
601
Brad Ebingered690772018-01-23 13:41:32 -0800602 public void onSmsReady() throws RemoteException {
603 synchronized (mLock) {
604 checkServiceIsReady();
605 getServiceInterface(mBinder).onSmsReady();
606 }
607 }
608
Brad Ebinger936a7d12018-01-16 09:36:56 -0800609 public void setSmsListener(IImsSmsListener listener) throws RemoteException {
610 synchronized (mLock) {
611 checkServiceIsReady();
612 getServiceInterface(mBinder).setSmsListener(listener);
613 }
614 }
615
Brad Ebingerd449b232018-02-12 14:33:05 -0800616 public @MmTelFeature.ProcessCallResult int shouldProcessCall(boolean isEmergency,
617 String[] numbers) throws RemoteException {
618 if (isEmergency && !isEmergencyMmTelAvailable()) {
619 // Don't query the ImsService if emergency calling is not available on the ImsService.
620 Log.i(TAG, "MmTel does not support emergency over IMS, fallback to CS.");
621 return MmTelFeature.PROCESS_CALL_CSFB;
622 }
623 synchronized (mLock) {
624 checkServiceIsReady();
625 return getServiceInterface(mBinder).shouldProcessCall(numbers);
626 }
627 }
628
Brad Ebinger936a7d12018-01-16 09:36:56 -0800629 /**
630 * @return an integer describing the current Feature Status, defined in
631 * {@link ImsFeature.ImsState}.
632 */
633 public int getFeatureState() {
634 synchronized (mLock) {
635 if (isBinderAlive() && mFeatureStateCached != null) {
Brad Ebinger936a7d12018-01-16 09:36:56 -0800636 return mFeatureStateCached;
637 }
638 }
639 // Don't synchronize on Binder call.
640 Integer status = retrieveFeatureState();
641 synchronized (mLock) {
642 if (status == null) {
643 return ImsFeature.STATE_UNAVAILABLE;
644 }
645 // Cache only non-null value for feature status.
646 mFeatureStateCached = status;
647 }
648 Log.i(TAG, "getFeatureState - returning " + status);
649 return status;
650 }
651
652 /**
653 * Internal method used to retrieve the feature status from the corresponding ImsService.
654 */
655 private Integer retrieveFeatureState() {
656 if (mBinder != null) {
657 try {
658 return getServiceInterface(mBinder).getFeatureState();
659 } catch (RemoteException e) {
660 // Status check failed, don't update cache
661 }
662 }
663 return null;
664 }
665
666 /**
667 * @param c Callback that will fire when the feature status has changed.
668 */
669 public void setStatusCallback(IFeatureUpdate c) {
670 mStatusCallback = c;
671 }
672
673 /**
674 * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
675 * method returns false, it doesn't mean that the Binder connection is not available (use
676 * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
677 * at this time.
678 *
679 * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
680 * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}.
681 */
682 public boolean isBinderReady() {
683 return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY;
684 }
685
686 /**
687 * @return false if the binder connection is no longer alive.
688 */
689 public boolean isBinderAlive() {
690 return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
691 }
692
693 protected void checkServiceIsReady() throws RemoteException {
694 if (!isBinderReady()) {
695 throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
696 }
697 }
698
699 private IImsMmTelFeature getServiceInterface(IBinder b) {
700 return IImsMmTelFeature.Stub.asInterface(b);
701 }
702
703 protected void checkBinderConnection() throws RemoteException {
704 if (!isBinderAlive()) {
705 throw new RemoteException("ImsServiceProxy is not available for that feature.");
706 }
707 }
708}