blob: 07069219e1ebd4a7a8acc8d5db6656260cebad80 [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
Brad Ebinger1050b072018-04-17 16:50:51 -070047import java.util.Collections;
Brad Ebinger936a7d12018-01-16 09:36:56 -080048import java.util.HashSet;
49import java.util.Set;
Brad Ebinger1050b072018-04-17 16:50:51 -070050import java.util.concurrent.ConcurrentHashMap;
Brad Ebinger936a7d12018-01-16 09:36:56 -080051
52/**
53 * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
54 * the platform currently supports: MMTel and RCS.
55 * @hide
56 */
57
58public class MmTelFeatureConnection {
59
60 protected static final String TAG = "MmTelFeatureConnection";
61 protected final int mSlotId;
62 protected IBinder mBinder;
63 private Context mContext;
64
Brad Ebingerfe2b2222018-03-08 16:16:15 -080065 private volatile boolean mIsAvailable = false;
Brad Ebinger936a7d12018-01-16 09:36:56 -080066 // ImsFeature Status from the ImsService. Cached.
67 private Integer mFeatureStateCached = null;
68 private IFeatureUpdate mStatusCallback;
69 private final Object mLock = new Object();
Brad Ebingerfe2b2222018-03-08 16:16:15 -080070 // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent.
71 private boolean mSupportsEmergencyCalling = false;
Brad Ebinger936a7d12018-01-16 09:36:56 -080072
Brad Ebinger48632582018-03-16 11:46:30 -070073 // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If
74 // it becomes disconnected, invalidate.
75 private IImsRegistration mRegistrationBinder;
76 private IImsConfig mConfigBinder;
Brad Ebinger936a7d12018-01-16 09:36:56 -080077
78 private abstract class CallbackAdapterManager<T> {
79 private static final String TAG = "CallbackAdapterManager";
80
Brad Ebinger1050b072018-04-17 16:50:51 -070081 protected final Set<T> mLocalCallbacks =
82 Collections.newSetFromMap(new ConcurrentHashMap<>());
Brad Ebinger936a7d12018-01-16 09:36:56 -080083 private boolean mHasConnected = false;
84
85 public void addCallback(T localCallback) throws RemoteException {
86 // We only one one binding to the ImsService per process.
87 // Store any more locally.
88 synchronized (mLock) {
Brad Ebinger1050b072018-04-17 16:50:51 -070089 if (!mHasConnected) {
Brad Ebinger936a7d12018-01-16 09:36:56 -080090 // throws a RemoteException if a connection can not be established.
Brad Ebinger1050b072018-04-17 16:50:51 -070091 if (createConnection()) {
Brad Ebinger936a7d12018-01-16 09:36:56 -080092 mHasConnected = true;
93 } else {
94 throw new RemoteException("Can not create connection!");
95 }
96 }
Brad Ebinger936a7d12018-01-16 09:36:56 -080097 }
Brad Ebinger1050b072018-04-17 16:50:51 -070098 Log.i(TAG, "Local callback added: " + localCallback);
99 mLocalCallbacks.add(localCallback);
Brad Ebinger936a7d12018-01-16 09:36:56 -0800100 }
101
102 public void removeCallback(T localCallback) {
103 // We only maintain one binding to the ImsService per process.
Brad Ebinger1050b072018-04-17 16:50:51 -0700104 Log.i(TAG, "Local callback removed: " + localCallback);
105 mLocalCallbacks.remove(localCallback);
Brad Ebinger936a7d12018-01-16 09:36:56 -0800106 synchronized (mLock) {
Brad Ebinger1050b072018-04-17 16:50:51 -0700107 // If we have removed all local callbacks, remove callback to ImsService.
Brad Ebinger936a7d12018-01-16 09:36:56 -0800108 if(mHasConnected) {
109 if (mLocalCallbacks.isEmpty()) {
110 removeConnection();
111 mHasConnected = false;
112 }
113 }
114 }
115 }
116
117 public void close() {
118 synchronized (mLock) {
119 if (mHasConnected) {
120 removeConnection();
121 // Still mark the connection as disconnected, even if this fails.
122 mHasConnected = false;
123 }
Brad Ebinger936a7d12018-01-16 09:36:56 -0800124 }
Brad Ebinger1050b072018-04-17 16:50:51 -0700125 Log.i(TAG, "Closing connection and clearing callbacks");
126 mLocalCallbacks.clear();
Brad Ebinger936a7d12018-01-16 09:36:56 -0800127 }
128
129 abstract boolean createConnection() throws RemoteException;
130
131 abstract void removeConnection();
132 }
133 private ImsRegistrationCallbackAdapter mRegistrationCallbackManager
134 = new ImsRegistrationCallbackAdapter();
135 private class ImsRegistrationCallbackAdapter
136 extends CallbackAdapterManager<ImsRegistrationImplBase.Callback> {
137 private final RegistrationCallbackAdapter mRegistrationCallbackAdapter
138 = new RegistrationCallbackAdapter();
139
140 private class RegistrationCallbackAdapter extends IImsRegistrationCallback.Stub {
141
142 @Override
143 public void onRegistered(int imsRadioTech) {
144 Log.i(TAG, "onRegistered ::");
145
Brad Ebinger1050b072018-04-17 16:50:51 -0700146 mLocalCallbacks.forEach(l -> l.onRegistered(imsRadioTech));
Brad Ebinger936a7d12018-01-16 09:36:56 -0800147 }
148
149 @Override
150 public void onRegistering(int imsRadioTech) {
151 Log.i(TAG, "onRegistering ::");
152
Brad Ebinger1050b072018-04-17 16:50:51 -0700153 mLocalCallbacks.forEach(l -> l.onRegistering(imsRadioTech));
Brad Ebinger936a7d12018-01-16 09:36:56 -0800154 }
155
156 @Override
157 public void onDeregistered(ImsReasonInfo imsReasonInfo) {
158 Log.i(TAG, "onDeregistered ::");
159
Brad Ebinger1050b072018-04-17 16:50:51 -0700160 mLocalCallbacks.forEach(l -> l.onDeregistered(imsReasonInfo));
Brad Ebinger936a7d12018-01-16 09:36:56 -0800161 }
162
163 @Override
164 public void onTechnologyChangeFailed(int targetRadioTech, ImsReasonInfo imsReasonInfo) {
165 Log.i(TAG, "onTechnologyChangeFailed :: targetAccessTech=" + targetRadioTech +
166 ", imsReasonInfo=" + imsReasonInfo);
167
Brad Ebinger936a7d12018-01-16 09:36:56 -0800168 mLocalCallbacks.forEach(l -> l.onTechnologyChangeFailed(targetRadioTech,
169 imsReasonInfo));
Brad Ebinger936a7d12018-01-16 09:36:56 -0800170 }
171
172 @Override
173 public void onSubscriberAssociatedUriChanged(Uri[] uris) {
174 Log.i(TAG, "onSubscriberAssociatedUriChanged");
Brad Ebinger1050b072018-04-17 16:50:51 -0700175
176 mLocalCallbacks.forEach(l -> l.onSubscriberAssociatedUriChanged(uris));
Brad Ebinger936a7d12018-01-16 09:36:56 -0800177 }
178 }
179
180 @Override
181 boolean createConnection() throws RemoteException {
182 IImsRegistration imsRegistration = getRegistration();
183 if (imsRegistration != null) {
184 getRegistration().addRegistrationCallback(mRegistrationCallbackAdapter);
185 return true;
186 } else {
187 Log.e(TAG, "ImsRegistration is null");
188 return false;
189 }
190 }
191
192 @Override
193 void removeConnection() {
194 IImsRegistration imsRegistration = getRegistration();
195 if (imsRegistration != null) {
196 try {
Brad Ebingerd1b1a3c2018-03-08 11:37:35 -0800197 getRegistration().removeRegistrationCallback(mRegistrationCallbackAdapter);
Brad Ebinger936a7d12018-01-16 09:36:56 -0800198 } catch (RemoteException e) {
199 Log.w(TAG, "removeConnection: couldn't remove registration callback");
200 }
201 } else {
202 Log.e(TAG, "ImsRegistration is null");
203 }
204 }
205 }
206
207 private final CapabilityCallbackManager mCapabilityCallbackManager
208 = new CapabilityCallbackManager();
209 private class CapabilityCallbackManager
210 extends CallbackAdapterManager<ImsFeature.CapabilityCallback> {
211 private final CapabilityCallbackAdapter mCallbackAdapter = new CapabilityCallbackAdapter();
212
213 private class CapabilityCallbackAdapter extends ImsFeature.CapabilityCallback {
214 // Called when the Capabilities Status on this connection have changed.
215 @Override
216 public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
Brad Ebinger1050b072018-04-17 16:50:51 -0700217 mLocalCallbacks.forEach(
218 callback -> callback.onCapabilitiesStatusChanged(config));
Brad Ebinger936a7d12018-01-16 09:36:56 -0800219 }
220 }
221
222 @Override
223 boolean createConnection() throws RemoteException {
Brad Ebinger1050b072018-04-17 16:50:51 -0700224 IImsMmTelFeature binder;
225 synchronized (mLock) {
226 checkServiceIsReady();
227 binder = getServiceInterface(mBinder);
228 }
Brad Ebinger936a7d12018-01-16 09:36:56 -0800229 if (binder != null) {
230 binder.addCapabilityCallback(mCallbackAdapter);
231 return true;
232 } else {
233 Log.w(TAG, "create: Couldn't get IImsMmTelFeature binder");
234 return false;
235 }
236 }
237
238 @Override
239 void removeConnection() {
Brad Ebinger1050b072018-04-17 16:50:51 -0700240 IImsMmTelFeature binder = null;
241 synchronized (mLock) {
242 try {
243 checkServiceIsReady();
244 binder = getServiceInterface(mBinder);
245 } catch (RemoteException e) {
246 // binder is null
247 }
248 }
Brad Ebinger936a7d12018-01-16 09:36:56 -0800249 if (binder != null) {
250 try {
251 binder.removeCapabilityCallback(mCallbackAdapter);
252 } catch (RemoteException e) {
253 Log.w(TAG, "remove: IImsMmTelFeature binder is dead");
254 }
255 } else {
256 Log.w(TAG, "remove: Couldn't get IImsMmTelFeature binder");
257 }
258 }
259 }
260
261
262 public static MmTelFeatureConnection create(Context context , int slotId) {
263 MmTelFeatureConnection serviceProxy = new MmTelFeatureConnection(context, slotId);
264
265 TelephonyManager tm = getTelephonyManager(context);
266 if (tm == null) {
267 Rlog.w(TAG, "create: TelephonyManager is null!");
268 // Binder can be unset in this case because it will be torn down/recreated as part of
269 // a retry mechanism until the serviceProxy binder is set successfully.
270 return serviceProxy;
271 }
272
273 IImsMmTelFeature binder = tm.getImsMmTelFeatureAndListen(slotId,
274 serviceProxy.getListener());
275 if (binder != null) {
276 serviceProxy.setBinder(binder.asBinder());
277 // Trigger the cache to be updated for feature status.
278 serviceProxy.getFeatureState();
279 } else {
280 Rlog.w(TAG, "create: binder is null! Slot Id: " + slotId);
281 }
282 return serviceProxy;
283 }
284
285 public static TelephonyManager getTelephonyManager(Context context) {
286 return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
287 }
288
289 public interface IFeatureUpdate {
290 /**
291 * Called when the ImsFeature has changed its state. Use
292 * {@link ImsFeature#getFeatureState()} to get the new state.
293 */
294 void notifyStateChanged();
295
296 /**
297 * Called when the ImsFeature has become unavailable due to the binder switching or app
298 * crashing. A new ImsServiceProxy should be requested for that feature.
299 */
300 void notifyUnavailable();
301 }
302
303 private final IImsServiceFeatureCallback mListenerBinder =
304 new IImsServiceFeatureCallback.Stub() {
305
306 @Override
307 public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800308 // The feature has been enabled. This happens when the feature is first created and may
309 // happen when the feature is re-enabled.
Brad Ebinger936a7d12018-01-16 09:36:56 -0800310 synchronized (mLock) {
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800311 if(mSlotId != slotId) {
312 return;
Brad Ebinger936a7d12018-01-16 09:36:56 -0800313 }
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800314 switch (feature) {
315 case ImsFeature.FEATURE_MMTEL: {
316 if (!mIsAvailable) {
317 Log.i(TAG, "MmTel enabled on slotId: " + slotId);
318 mIsAvailable = true;
319 }
320 break;
321 }
322 case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
323 mSupportsEmergencyCalling = true;
324 Log.i(TAG, "Emergency calling enabled on slotId: " + slotId);
325 break;
326 }
327 }
328
Brad Ebinger936a7d12018-01-16 09:36:56 -0800329 }
330 }
331
332 @Override
333 public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
334 synchronized (mLock) {
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800335 if(mSlotId != slotId) {
336 return;
337 }
338 switch (feature) {
339 case ImsFeature.FEATURE_MMTEL: {
340 if (mIsAvailable) {
341 Log.i(TAG, "MmTel disabled on slotId: " + slotId);
342 mIsAvailable = false;
Brad Ebinger48632582018-03-16 11:46:30 -0700343 mmTelFeatureRemoved();
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800344 if (mStatusCallback != null) {
345 mStatusCallback.notifyUnavailable();
346 }
347 }
348 break;
349 }
350 case ImsFeature.FEATURE_EMERGENCY_MMTEL : {
351 mSupportsEmergencyCalling = false;
352 Log.i(TAG, "Emergency calling disabled on slotId: " + slotId);
353 break;
Brad Ebinger936a7d12018-01-16 09:36:56 -0800354 }
355 }
356 }
357 }
358
359 @Override
360 public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
361 synchronized (mLock) {
362 Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
363 " status: " + status);
364 if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
365 mFeatureStateCached = status;
366 if (mStatusCallback != null) {
367 mStatusCallback.notifyStateChanged();
368 }
369 }
370 }
371 }
372 };
373
374 public MmTelFeatureConnection(Context context, int slotId) {
375 mSlotId = slotId;
376 mContext = context;
377 }
378
Brad Ebinger48632582018-03-16 11:46:30 -0700379 // Called when the MmTelFeatureConnection has received an unavailable notification.
380 private void mmTelFeatureRemoved() {
381 synchronized (mLock) {
382 // invalidate caches.
383 mRegistrationBinder = null;
384 mConfigBinder = null;
385 }
386 }
387
Brad Ebinger936a7d12018-01-16 09:36:56 -0800388 private @Nullable IImsRegistration getRegistration() {
Brad Ebinger48632582018-03-16 11:46:30 -0700389 synchronized (mLock) {
390 // null if cache is invalid;
391 if (mRegistrationBinder != null) {
392 return mRegistrationBinder;
393 }
394 }
Brad Ebinger936a7d12018-01-16 09:36:56 -0800395 TelephonyManager tm = getTelephonyManager(mContext);
Brad Ebinger48632582018-03-16 11:46:30 -0700396 // We don't want to synchronize on a binder call to another process.
397 IImsRegistration regBinder = tm != null
398 ? tm.getImsRegistration(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
399 synchronized (mLock) {
400 // mRegistrationBinder may have changed while we tried to get the registration
401 // interface.
402 if (mRegistrationBinder == null) {
403 mRegistrationBinder = regBinder;
404 }
405 }
406 return mRegistrationBinder;
Brad Ebinger936a7d12018-01-16 09:36:56 -0800407 }
408
409 private IImsConfig getConfig() {
Brad Ebinger48632582018-03-16 11:46:30 -0700410 synchronized (mLock) {
411 // null if cache is invalid;
412 if (mConfigBinder != null) {
413 return mConfigBinder;
414 }
415 }
Brad Ebinger936a7d12018-01-16 09:36:56 -0800416 TelephonyManager tm = getTelephonyManager(mContext);
Brad Ebinger48632582018-03-16 11:46:30 -0700417 IImsConfig configBinder = tm != null
418 ? tm.getImsConfig(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
419 synchronized (mLock) {
420 // mConfigBinder may have changed while we tried to get the config interface.
421 if (mConfigBinder == null) {
422 mConfigBinder = configBinder;
423 }
424 }
425 return mConfigBinder;
Brad Ebinger936a7d12018-01-16 09:36:56 -0800426 }
427
Brad Ebingerd449b232018-02-12 14:33:05 -0800428 public boolean isEmergencyMmTelAvailable() {
Brad Ebingerfe2b2222018-03-08 16:16:15 -0800429 return mSupportsEmergencyCalling;
Brad Ebingerd449b232018-02-12 14:33:05 -0800430 }
431
Brad Ebinger936a7d12018-01-16 09:36:56 -0800432 public IImsServiceFeatureCallback getListener() {
433 return mListenerBinder;
434 }
435
436 public void setBinder(IBinder binder) {
437 mBinder = binder;
438 }
439
440 /**
441 * Opens the connection to the {@link MmTelFeature} and establishes a listener back to the
442 * framework. Calling this method multiple times will reset the listener attached to the
443 * {@link MmTelFeature}.
444 * @param listener A {@link MmTelFeature.Listener} that will be used by the {@link MmTelFeature}
445 * to notify the framework of updates.
446 */
447 public void openConnection(MmTelFeature.Listener listener) throws RemoteException {
448 synchronized (mLock) {
449 checkServiceIsReady();
Brad Ebinger48632582018-03-16 11:46:30 -0700450 getServiceInterface(mBinder).setListener(listener);
Brad Ebinger936a7d12018-01-16 09:36:56 -0800451 }
452 }
453
454 public void closeConnection() {
455 mRegistrationCallbackManager.close();
456 mCapabilityCallbackManager.close();
457 try {
Brad Ebinger23916132018-04-06 09:37:50 -0700458 synchronized (mLock) {
459 if (isBinderAlive()) {
460 getServiceInterface(mBinder).setListener(null);
461 }
462 }
Brad Ebinger936a7d12018-01-16 09:36:56 -0800463 } catch (RemoteException e) {
464 Log.w(TAG, "closeConnection: couldn't remove listener!");
465 }
466 }
467
468 public void addRegistrationCallback(ImsRegistrationImplBase.Callback callback)
469 throws RemoteException {
470 mRegistrationCallbackManager.addCallback(callback);
471 }
472
473 public void removeRegistrationCallback(ImsRegistrationImplBase.Callback callback)
474 throws RemoteException {
475 mRegistrationCallbackManager.removeCallback(callback);
476 }
477
478 public void addCapabilityCallback(ImsFeature.CapabilityCallback callback)
479 throws RemoteException {
480 mCapabilityCallbackManager.addCallback(callback);
481 }
482
483 public void removeCapabilityCallback(ImsFeature.CapabilityCallback callback)
484 throws RemoteException {
485 mCapabilityCallbackManager.removeCallback(callback);
486 }
487
488 public void changeEnabledCapabilities(CapabilityChangeRequest request,
489 ImsFeature.CapabilityCallback callback) throws RemoteException {
490 synchronized (mLock) {
491 checkServiceIsReady();
492 getServiceInterface(mBinder).changeCapabilitiesConfiguration(request, callback);
493 }
494 }
495
496 public void queryEnabledCapabilities(int capability, int radioTech,
497 ImsFeature.CapabilityCallback callback) throws RemoteException {
498 synchronized (mLock) {
499 checkServiceIsReady();
500 getServiceInterface(mBinder).queryCapabilityConfiguration(capability, radioTech,
501 callback);
502 }
503 }
504
505 public MmTelFeature.MmTelCapabilities queryCapabilityStatus() throws RemoteException {
506 synchronized (mLock) {
507 checkServiceIsReady();
508 return new MmTelFeature.MmTelCapabilities(
509 getServiceInterface(mBinder).queryCapabilityStatus());
510 }
511 }
512
513 public ImsCallProfile createCallProfile(int callServiceType, int callType)
514 throws RemoteException {
515 synchronized (mLock) {
516 checkServiceIsReady();
517 return getServiceInterface(mBinder).createCallProfile(callServiceType, callType);
518 }
519 }
520
521 public IImsCallSession createCallSession(ImsCallProfile profile)
522 throws RemoteException {
523 synchronized (mLock) {
524 checkServiceIsReady();
525 return getServiceInterface(mBinder).createCallSession(profile);
526 }
527 }
528
529 public IImsUt getUtInterface() throws RemoteException {
530 synchronized (mLock) {
531 checkServiceIsReady();
532 return getServiceInterface(mBinder).getUtInterface();
533 }
534 }
535
536 public IImsConfig getConfigInterface() throws RemoteException {
Brad Ebinger57f6a852018-03-08 13:14:38 -0800537 return getConfig();
Brad Ebinger936a7d12018-01-16 09:36:56 -0800538 }
539
540 public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech()
Brad Ebinger57f6a852018-03-08 13:14:38 -0800541 throws RemoteException {
542 IImsRegistration registration = getRegistration();
543 if (registration != null) {
Brad Ebinger936a7d12018-01-16 09:36:56 -0800544 return registration.getRegistrationTechnology();
Brad Ebinger57f6a852018-03-08 13:14:38 -0800545 } else {
546 return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
Brad Ebinger936a7d12018-01-16 09:36:56 -0800547 }
548 }
549
550 public IImsEcbm getEcbmInterface() throws RemoteException {
551 synchronized (mLock) {
552 checkServiceIsReady();
553 return getServiceInterface(mBinder).getEcbmInterface();
554 }
555 }
556
557 public void setUiTTYMode(int uiTtyMode, Message onComplete)
558 throws RemoteException {
559 synchronized (mLock) {
560 checkServiceIsReady();
561 getServiceInterface(mBinder).setUiTtyMode(uiTtyMode, onComplete);
562 }
563 }
564
565 public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
566 synchronized (mLock) {
567 checkServiceIsReady();
568 return getServiceInterface(mBinder).getMultiEndpointInterface();
569 }
570 }
571
572 public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
573 byte[] pdu) throws RemoteException {
574 synchronized (mLock) {
575 checkServiceIsReady();
576 getServiceInterface(mBinder).sendSms(token, messageRef, format, smsc, isRetry,
577 pdu);
578 }
579 }
580
581 public void acknowledgeSms(int token, int messageRef,
582 @ImsSmsImplBase.SendStatusResult int result) throws RemoteException {
583 synchronized (mLock) {
584 checkServiceIsReady();
585 getServiceInterface(mBinder).acknowledgeSms(token, messageRef, result);
586 }
587 }
588
589 public void acknowledgeSmsReport(int token, int messageRef,
590 @ImsSmsImplBase.StatusReportResult int result) throws RemoteException {
591 synchronized (mLock) {
592 checkServiceIsReady();
593 getServiceInterface(mBinder).acknowledgeSmsReport(token, messageRef, result);
594 }
595 }
596
597 public String getSmsFormat() throws RemoteException {
598 synchronized (mLock) {
599 checkServiceIsReady();
600 return getServiceInterface(mBinder).getSmsFormat();
601 }
602 }
603
Brad Ebingered690772018-01-23 13:41:32 -0800604 public void onSmsReady() throws RemoteException {
605 synchronized (mLock) {
606 checkServiceIsReady();
607 getServiceInterface(mBinder).onSmsReady();
608 }
609 }
610
Brad Ebinger936a7d12018-01-16 09:36:56 -0800611 public void setSmsListener(IImsSmsListener listener) throws RemoteException {
612 synchronized (mLock) {
613 checkServiceIsReady();
614 getServiceInterface(mBinder).setSmsListener(listener);
615 }
616 }
617
Brad Ebingerd449b232018-02-12 14:33:05 -0800618 public @MmTelFeature.ProcessCallResult int shouldProcessCall(boolean isEmergency,
619 String[] numbers) throws RemoteException {
620 if (isEmergency && !isEmergencyMmTelAvailable()) {
621 // Don't query the ImsService if emergency calling is not available on the ImsService.
622 Log.i(TAG, "MmTel does not support emergency over IMS, fallback to CS.");
623 return MmTelFeature.PROCESS_CALL_CSFB;
624 }
625 synchronized (mLock) {
626 checkServiceIsReady();
627 return getServiceInterface(mBinder).shouldProcessCall(numbers);
628 }
629 }
630
Brad Ebinger936a7d12018-01-16 09:36:56 -0800631 /**
632 * @return an integer describing the current Feature Status, defined in
633 * {@link ImsFeature.ImsState}.
634 */
635 public int getFeatureState() {
636 synchronized (mLock) {
637 if (isBinderAlive() && mFeatureStateCached != null) {
Brad Ebinger936a7d12018-01-16 09:36:56 -0800638 return mFeatureStateCached;
639 }
640 }
641 // Don't synchronize on Binder call.
642 Integer status = retrieveFeatureState();
643 synchronized (mLock) {
644 if (status == null) {
645 return ImsFeature.STATE_UNAVAILABLE;
646 }
647 // Cache only non-null value for feature status.
648 mFeatureStateCached = status;
649 }
650 Log.i(TAG, "getFeatureState - returning " + status);
651 return status;
652 }
653
654 /**
655 * Internal method used to retrieve the feature status from the corresponding ImsService.
656 */
657 private Integer retrieveFeatureState() {
658 if (mBinder != null) {
659 try {
660 return getServiceInterface(mBinder).getFeatureState();
661 } catch (RemoteException e) {
662 // Status check failed, don't update cache
663 }
664 }
665 return null;
666 }
667
668 /**
669 * @param c Callback that will fire when the feature status has changed.
670 */
671 public void setStatusCallback(IFeatureUpdate c) {
672 mStatusCallback = c;
673 }
674
675 /**
676 * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
677 * method returns false, it doesn't mean that the Binder connection is not available (use
678 * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
679 * at this time.
680 *
681 * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
682 * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}.
683 */
684 public boolean isBinderReady() {
685 return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY;
686 }
687
688 /**
689 * @return false if the binder connection is no longer alive.
690 */
691 public boolean isBinderAlive() {
692 return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
693 }
694
695 protected void checkServiceIsReady() throws RemoteException {
696 if (!isBinderReady()) {
697 throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
698 }
699 }
700
701 private IImsMmTelFeature getServiceInterface(IBinder b) {
702 return IImsMmTelFeature.Stub.asInterface(b);
703 }
704
705 protected void checkBinderConnection() throws RemoteException {
706 if (!isBinderAlive()) {
707 throw new RemoteException("ImsServiceProxy is not available for that feature.");
708 }
709 }
710}