1 /* 2 * Copyright (C) 2018 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.internal.telephony.ims; 18 19 import android.app.PendingIntent; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.net.Uri; 25 import android.os.Message; 26 import android.os.RemoteException; 27 import android.telephony.TelephonyManager; 28 import android.telephony.ims.ImsCallProfile; 29 import android.telephony.ims.ImsReasonInfo; 30 import android.telephony.ims.feature.CapabilityChangeRequest; 31 import android.telephony.ims.feature.MmTelFeature; 32 import android.telephony.ims.stub.ImsRegistrationImplBase; 33 import android.util.Log; 34 35 import com.android.ims.ImsConfigListener; 36 import com.android.ims.ImsManager; 37 import com.android.ims.internal.IImsCallSession; 38 import com.android.ims.internal.IImsConfig; 39 import com.android.ims.internal.IImsEcbm; 40 import com.android.ims.internal.IImsMultiEndpoint; 41 import com.android.ims.internal.IImsRegistrationListener; 42 import com.android.ims.internal.IImsUt; 43 44 import java.util.HashMap; 45 import java.util.Map; 46 import java.util.concurrent.CountDownLatch; 47 import java.util.concurrent.TimeUnit; 48 49 public class MmTelFeatureCompatAdapter extends MmTelFeature { 50 51 private static final String TAG = "MmTelFeatureCompat"; 52 53 public static final String ACTION_IMS_INCOMING_CALL = "com.android.ims.IMS_INCOMING_CALL"; 54 55 private static final int WAIT_TIMEOUT_MS = 2000; 56 57 private final MmTelInterfaceAdapter mCompatFeature; 58 private ImsRegistrationCompatAdapter mRegCompatAdapter; 59 private int mSessionId = -1; 60 61 private static final Map<Integer, Integer> REG_TECH_TO_NET_TYPE = new HashMap<>(2); 62 63 static { REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_NR, TelephonyManager.NETWORK_TYPE_NR)64 REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_NR, 65 TelephonyManager.NETWORK_TYPE_NR); REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, TelephonyManager.NETWORK_TYPE_LTE)66 REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, 67 TelephonyManager.NETWORK_TYPE_LTE); REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, TelephonyManager.NETWORK_TYPE_IWLAN)68 REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, 69 TelephonyManager.NETWORK_TYPE_IWLAN); REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM, TelephonyManager.NETWORK_TYPE_IWLAN)70 REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM, 71 TelephonyManager.NETWORK_TYPE_IWLAN); 72 } 73 74 // Feature Type for compatibility with old "feature" updates 75 public static final int FEATURE_TYPE_UNKNOWN = -1; 76 public static final int FEATURE_TYPE_VOICE_OVER_LTE = 0; 77 public static final int FEATURE_TYPE_VIDEO_OVER_LTE = 1; 78 public static final int FEATURE_TYPE_VOICE_OVER_WIFI = 2; 79 public static final int FEATURE_TYPE_VIDEO_OVER_WIFI = 3; 80 public static final int FEATURE_TYPE_UT_OVER_LTE = 4; 81 public static final int FEATURE_TYPE_UT_OVER_WIFI = 5; 82 83 public static final int FEATURE_UNKNOWN = -1; 84 public static final int FEATURE_DISABLED = 0; 85 public static final int FEATURE_ENABLED = 1; 86 87 private static class ConfigListener extends ImsConfigListener.Stub { 88 89 private final int mCapability; 90 private final int mTech; 91 private final CountDownLatch mLatch; 92 ConfigListener(int capability, int tech, CountDownLatch latch)93 public ConfigListener(int capability, int tech, CountDownLatch latch) { 94 mCapability = capability; 95 mTech = tech; 96 mLatch = latch; 97 } 98 99 @Override onGetFeatureResponse(int feature, int network, int value, int status)100 public void onGetFeatureResponse(int feature, int network, int value, int status) 101 throws RemoteException { 102 if (feature == mCapability && network == mTech) { 103 mLatch.countDown(); 104 getFeatureValueReceived(value); 105 } else { 106 Log.i(TAG, "onGetFeatureResponse: response different than requested: feature=" 107 + feature + " and network=" + network); 108 } 109 } 110 111 @Override onSetFeatureResponse(int feature, int network, int value, int status)112 public void onSetFeatureResponse(int feature, int network, int value, int status) 113 throws RemoteException { 114 if (feature == mCapability && network == mTech) { 115 mLatch.countDown(); 116 setFeatureValueReceived(value); 117 } else { 118 Log.i(TAG, "onSetFeatureResponse: response different than requested: feature=" 119 + feature + " and network=" + network); 120 } 121 } 122 123 @Override onGetVideoQuality(int status, int quality)124 public void onGetVideoQuality(int status, int quality) throws RemoteException { 125 } 126 127 @Override onSetVideoQuality(int status)128 public void onSetVideoQuality(int status) throws RemoteException { 129 } 130 getFeatureValueReceived(int value)131 public void getFeatureValueReceived(int value) { 132 } 133 setFeatureValueReceived(int value)134 public void setFeatureValueReceived(int value) { 135 } 136 } 137 138 // Trampolines "old" listener events to the new interface. 139 private final IImsRegistrationListener mListener = new IImsRegistrationListener.Stub() { 140 @Override 141 public void registrationConnected() throws RemoteException { 142 // Implemented in the Registration Adapter 143 } 144 145 @Override 146 public void registrationProgressing() throws RemoteException { 147 // Implemented in the Registration Adapter 148 } 149 150 @Override 151 public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException { 152 // Implemented in the Registration Adapter 153 } 154 155 @Override 156 public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException { 157 // Implemented in the Registration Adapter 158 } 159 160 @Override 161 public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException { 162 // At de-registration, notify the framework that no IMS capabilities are currently 163 // available. 164 Log.i(TAG, "registrationDisconnected: resetting MMTEL capabilities."); 165 notifyCapabilitiesStatusChanged(new MmTelCapabilities()); 166 // Implemented in the Registration Adapter 167 } 168 169 @Override 170 public void registrationResumed() throws RemoteException { 171 // Don't care 172 } 173 174 @Override 175 public void registrationSuspended() throws RemoteException { 176 // Don't care 177 } 178 179 @Override 180 public void registrationServiceCapabilityChanged(int serviceClass, int event) 181 throws RemoteException { 182 // Don't care 183 } 184 185 @Override 186 public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures, 187 int[] disabledFeatures) throws RemoteException { 188 notifyCapabilitiesStatusChanged(convertCapabilities(enabledFeatures)); 189 } 190 191 @Override 192 public void voiceMessageCountUpdate(int count) throws RemoteException { 193 notifyVoiceMessageCountUpdate(count); 194 } 195 196 @Override 197 public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException { 198 // Implemented in the Registration Adapter 199 } 200 201 @Override 202 public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo) 203 throws RemoteException { 204 // Implemented in the Registration Adapter 205 } 206 }; 207 208 /** 209 * Stub implementation of the "old" Registration listener interface that provides no 210 * functionality. Instead, it is used to ensure compatibility with older devices that require 211 * a listener on startSession. The actual Registration Listener Interface is added separately 212 * in ImsRegistration. 213 */ 214 private class ImsRegistrationListenerBase extends IImsRegistrationListener.Stub { 215 216 @Override registrationConnected()217 public void registrationConnected() throws RemoteException { 218 } 219 220 @Override registrationProgressing()221 public void registrationProgressing() throws RemoteException { 222 } 223 224 @Override registrationConnectedWithRadioTech(int imsRadioTech)225 public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException { 226 } 227 228 @Override registrationProgressingWithRadioTech(int imsRadioTech)229 public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException { 230 } 231 232 @Override registrationDisconnected(ImsReasonInfo imsReasonInfo)233 public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException { 234 } 235 236 @Override registrationResumed()237 public void registrationResumed() throws RemoteException { 238 } 239 240 @Override registrationSuspended()241 public void registrationSuspended() throws RemoteException { 242 } 243 244 @Override registrationServiceCapabilityChanged(int serviceClass, int event)245 public void registrationServiceCapabilityChanged(int serviceClass, int event) 246 throws RemoteException { 247 } 248 249 @Override registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures, int[] disabledFeatures)250 public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures, 251 int[] disabledFeatures) throws RemoteException { 252 } 253 254 @Override voiceMessageCountUpdate(int count)255 public void voiceMessageCountUpdate(int count) throws RemoteException { 256 } 257 258 @Override registrationAssociatedUriChanged(Uri[] uris)259 public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException { 260 } 261 262 @Override registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)263 public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo) 264 throws RemoteException { 265 } 266 } 267 268 // Handle Incoming Call as PendingIntent, the old method 269 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 270 @Override 271 public void onReceive(Context context, Intent intent) { 272 Log.i(TAG, "onReceive"); 273 if (intent.getAction().equals(ACTION_IMS_INCOMING_CALL)) { 274 Log.i(TAG, "onReceive : incoming call intent."); 275 276 String callId = intent.getStringExtra("android:imsCallID"); 277 try { 278 IImsCallSession session = mCompatFeature.getPendingCallSession(mSessionId, 279 callId); 280 notifyIncomingCallSession(session, intent.getExtras()); 281 } catch (RemoteException e) { 282 Log.w(TAG, "onReceive: Couldn't get Incoming call session."); 283 } 284 } 285 } 286 }; 287 MmTelFeatureCompatAdapter(Context context, int slotId, MmTelInterfaceAdapter compatFeature)288 public MmTelFeatureCompatAdapter(Context context, int slotId, 289 MmTelInterfaceAdapter compatFeature) { 290 initialize(context, slotId); 291 mCompatFeature = compatFeature; 292 } 293 294 @Override queryCapabilityConfiguration(int capability, int radioTech)295 public boolean queryCapabilityConfiguration(int capability, int radioTech) { 296 int capConverted = convertCapability(capability, radioTech); 297 // Wait for the result from the ImsService 298 CountDownLatch latch = new CountDownLatch(1); 299 final int[] returnValue = new int[1]; 300 returnValue[0] = FEATURE_UNKNOWN; 301 int regTech = REG_TECH_TO_NET_TYPE.getOrDefault(radioTech, 302 ImsRegistrationImplBase.REGISTRATION_TECH_NONE); 303 try { 304 mCompatFeature.getConfigInterface().getFeatureValue(capConverted, regTech, 305 new ConfigListener(capConverted, regTech, latch) { 306 @Override 307 public void getFeatureValueReceived(int value) { 308 returnValue[0] = value; 309 } 310 }); 311 } catch (RemoteException e) { 312 Log.w(TAG, "queryCapabilityConfiguration"); 313 } 314 try { 315 latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 316 } catch (InterruptedException e) { 317 Log.w(TAG, "queryCapabilityConfiguration - error waiting: " + e.getMessage()); 318 } 319 return returnValue[0] == FEATURE_ENABLED; 320 } 321 322 @Override changeEnabledCapabilities(CapabilityChangeRequest request, CapabilityCallbackProxy c)323 public void changeEnabledCapabilities(CapabilityChangeRequest request, 324 CapabilityCallbackProxy c) { 325 if (request == null) { 326 return; 327 } 328 try { 329 IImsConfig imsConfig = mCompatFeature.getConfigInterface(); 330 // Disable Capabilities 331 for (CapabilityChangeRequest.CapabilityPair cap : request.getCapabilitiesToDisable()) { 332 CountDownLatch latch = new CountDownLatch(1); 333 int capConverted = convertCapability(cap.getCapability(), cap.getRadioTech()); 334 int radioTechConverted = REG_TECH_TO_NET_TYPE.getOrDefault(cap.getRadioTech(), 335 ImsRegistrationImplBase.REGISTRATION_TECH_NONE); 336 Log.i(TAG, "changeEnabledCapabilities - cap: " + capConverted + " radioTech: " 337 + radioTechConverted + " disabled"); 338 imsConfig.setFeatureValue(capConverted, radioTechConverted, FEATURE_DISABLED, 339 new ConfigListener(capConverted, radioTechConverted, latch) { 340 @Override 341 public void setFeatureValueReceived(int value) { 342 if (value != FEATURE_DISABLED) { 343 if (c == null) { 344 return; 345 } 346 c.onChangeCapabilityConfigurationError(cap.getCapability(), 347 cap.getRadioTech(), CAPABILITY_ERROR_GENERIC); 348 } 349 Log.i(TAG, "changeEnabledCapabilities - setFeatureValueReceived" 350 + " with value " + value); 351 } 352 }); 353 latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 354 } 355 // Enable Capabilities 356 for (CapabilityChangeRequest.CapabilityPair cap : request.getCapabilitiesToEnable()) { 357 CountDownLatch latch = new CountDownLatch(1); 358 int capConverted = convertCapability(cap.getCapability(), cap.getRadioTech()); 359 int radioTechConverted = REG_TECH_TO_NET_TYPE.getOrDefault(cap.getRadioTech(), 360 ImsRegistrationImplBase.REGISTRATION_TECH_NONE); 361 Log.i(TAG, "changeEnabledCapabilities - cap: " + capConverted + " radioTech: " 362 + radioTechConverted + " enabled"); 363 imsConfig.setFeatureValue(capConverted, radioTechConverted, FEATURE_ENABLED, 364 new ConfigListener(capConverted, radioTechConverted, latch) { 365 @Override 366 public void setFeatureValueReceived(int value) { 367 if (value != FEATURE_ENABLED) { 368 if (c == null) { 369 return; 370 } 371 c.onChangeCapabilityConfigurationError(cap.getCapability(), 372 cap.getRadioTech(), CAPABILITY_ERROR_GENERIC); 373 } 374 Log.i(TAG, "changeEnabledCapabilities - setFeatureValueReceived" 375 + " with value " + value); 376 } 377 }); 378 latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 379 } 380 } catch (RemoteException | InterruptedException e) { 381 Log.w(TAG, "changeEnabledCapabilities: Error processing: " + e.getMessage()); 382 } 383 } 384 385 @Override createCallProfile(int callSessionType, int callType)386 public ImsCallProfile createCallProfile(int callSessionType, int callType) { 387 try { 388 return mCompatFeature.createCallProfile(mSessionId, callSessionType, callType); 389 } catch (RemoteException e) { 390 throw new RuntimeException(e.getMessage()); 391 } 392 } 393 394 @Override createCallSessionInterface(ImsCallProfile profile)395 public IImsCallSession createCallSessionInterface(ImsCallProfile profile) 396 throws RemoteException { 397 return mCompatFeature.createCallSession(mSessionId, profile); 398 } 399 400 @Override getUtInterface()401 public IImsUt getUtInterface() throws RemoteException { 402 return mCompatFeature.getUtInterface(); 403 } 404 405 @Override getEcbmInterface()406 public IImsEcbm getEcbmInterface() throws RemoteException { 407 return mCompatFeature.getEcbmInterface(); 408 } 409 410 @Override getMultiEndpointInterface()411 public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { 412 return mCompatFeature.getMultiEndpointInterface(); 413 } 414 415 @Override getFeatureState()416 public int getFeatureState() { 417 try { 418 return mCompatFeature.getFeatureState(); 419 } catch (RemoteException e) { 420 throw new RuntimeException(e.getMessage()); 421 } 422 } 423 424 @Override setUiTtyMode(int mode, Message onCompleteMessage)425 public void setUiTtyMode(int mode, Message onCompleteMessage) { 426 try { 427 mCompatFeature.setUiTTYMode(mode, onCompleteMessage); 428 } catch (RemoteException e) { 429 throw new RuntimeException(e.getMessage()); 430 } 431 } 432 433 434 @Override onFeatureRemoved()435 public void onFeatureRemoved() { 436 mContext.unregisterReceiver(mReceiver); 437 try { 438 mCompatFeature.endSession(mSessionId); 439 mCompatFeature.removeRegistrationListener(mListener); 440 if (mRegCompatAdapter != null) { 441 mCompatFeature.removeRegistrationListener( 442 mRegCompatAdapter.getRegistrationListener()); 443 } 444 } catch (RemoteException e) { 445 Log.w(TAG, "onFeatureRemoved: Couldn't end session: " + e.getMessage()); 446 } 447 } 448 449 @Override onFeatureReady()450 public void onFeatureReady() { 451 Log.i(TAG, "onFeatureReady called!"); 452 // This gets called when MmTelFeature.setListener is called. We need to use this time to 453 // call openSession on the old MMTelFeature implementation. 454 IntentFilter intentFilter = new IntentFilter(ImsManager.ACTION_IMS_INCOMING_CALL); 455 mContext.registerReceiver(mReceiver, intentFilter); 456 try { 457 mSessionId = mCompatFeature.startSession(createIncomingCallPendingIntent(), 458 new ImsRegistrationListenerBase()); 459 mCompatFeature.addRegistrationListener(mListener); 460 mCompatFeature.addRegistrationListener(mRegCompatAdapter.getRegistrationListener()); 461 } catch (RemoteException e) { 462 Log.e(TAG, "Couldn't start compat feature: " + e.getMessage()); 463 } 464 } 465 enableIms()466 public void enableIms() throws RemoteException { 467 mCompatFeature.turnOnIms(); 468 } 469 disableIms()470 public void disableIms() throws RemoteException { 471 mCompatFeature.turnOffIms(); 472 } 473 getOldConfigInterface()474 public IImsConfig getOldConfigInterface() { 475 try { 476 return mCompatFeature.getConfigInterface(); 477 } catch (RemoteException e) { 478 Log.w(TAG, "getOldConfigInterface(): " + e.getMessage()); 479 return null; 480 } 481 } 482 addRegistrationAdapter(ImsRegistrationCompatAdapter regCompat)483 public void addRegistrationAdapter(ImsRegistrationCompatAdapter regCompat) 484 throws RemoteException { 485 mRegCompatAdapter = regCompat; 486 } 487 convertCapabilities(int[] enabledFeatures)488 private MmTelCapabilities convertCapabilities(int[] enabledFeatures) { 489 boolean[] featuresEnabled = new boolean[enabledFeatures.length]; 490 for (int i = FEATURE_TYPE_VOICE_OVER_LTE; i <= FEATURE_TYPE_UT_OVER_WIFI 491 && i < enabledFeatures.length; i++) { 492 if (enabledFeatures[i] == i) { 493 featuresEnabled[i] = true; 494 } else if (enabledFeatures[i] == FEATURE_TYPE_UNKNOWN) { 495 // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled. 496 featuresEnabled[i] = false; 497 } 498 } 499 MmTelCapabilities capabilities = new MmTelCapabilities(); 500 if (featuresEnabled[FEATURE_TYPE_VOICE_OVER_LTE] 501 || featuresEnabled[FEATURE_TYPE_VOICE_OVER_WIFI]) { 502 // voice is enabled 503 capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_VOICE); 504 } 505 if (featuresEnabled[FEATURE_TYPE_VIDEO_OVER_LTE] 506 || featuresEnabled[FEATURE_TYPE_VIDEO_OVER_WIFI]) { 507 // video is enabled 508 capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_VIDEO); 509 } 510 if (featuresEnabled[FEATURE_TYPE_UT_OVER_LTE] 511 || featuresEnabled[FEATURE_TYPE_UT_OVER_WIFI]) { 512 // ut is enabled 513 capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_UT); 514 } 515 Log.i(TAG, "convertCapabilities - capabilities: " + capabilities); 516 return capabilities; 517 } 518 createIncomingCallPendingIntent()519 private PendingIntent createIncomingCallPendingIntent() { 520 Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL); 521 intent.setPackage(TelephonyManager.PHONE_PROCESS_NAME); 522 return PendingIntent.getBroadcast(mContext, 0, intent, 523 // Mutable because information associated with the call is passed back here. 524 PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); 525 } 526 convertCapability(int capability, int radioTech)527 private int convertCapability(int capability, int radioTech) { 528 int capConverted = FEATURE_TYPE_UNKNOWN; 529 if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { 530 switch (capability) { 531 case MmTelCapabilities.CAPABILITY_TYPE_VOICE: 532 capConverted = FEATURE_TYPE_VOICE_OVER_LTE; 533 break; 534 case MmTelCapabilities.CAPABILITY_TYPE_VIDEO: 535 capConverted = FEATURE_TYPE_VIDEO_OVER_LTE; 536 break; 537 case MmTelCapabilities.CAPABILITY_TYPE_UT: 538 capConverted = FEATURE_TYPE_UT_OVER_LTE; 539 break; 540 } 541 } else if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) { 542 switch (capability) { 543 case MmTelCapabilities.CAPABILITY_TYPE_VOICE: 544 capConverted = FEATURE_TYPE_VOICE_OVER_WIFI; 545 break; 546 case MmTelCapabilities.CAPABILITY_TYPE_VIDEO: 547 capConverted = FEATURE_TYPE_VIDEO_OVER_WIFI; 548 break; 549 case MmTelCapabilities.CAPABILITY_TYPE_UT: 550 capConverted = FEATURE_TYPE_UT_OVER_WIFI; 551 break; 552 } 553 } 554 return capConverted; 555 } 556 } 557