1 /* 2 * Copyright (C) 2019 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.Nullable; 20 import android.content.Context; 21 import android.os.IBinder; 22 import android.os.Looper; 23 import android.os.RemoteException; 24 import android.telephony.TelephonyManager; 25 import android.telephony.ims.ImsService; 26 import android.telephony.ims.aidl.IImsConfig; 27 import android.telephony.ims.aidl.IImsRegistration; 28 import android.telephony.ims.aidl.ISipTransport; 29 import android.telephony.ims.feature.ImsFeature; 30 import android.telephony.ims.stub.ImsRegistrationImplBase; 31 import android.util.Log; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 35 import java.util.NoSuchElementException; 36 37 /** 38 * Base class of MmTelFeatureConnection and RcsFeatureConnection. 39 */ 40 public abstract class FeatureConnection { 41 protected static final String TAG = "FeatureConnection"; 42 43 protected static boolean sImsSupportedOnDevice = true; 44 45 protected final int mSlotId; 46 protected Context mContext; 47 protected IBinder mBinder; 48 49 // We are assuming the feature is available when started. 50 protected volatile boolean mIsAvailable = true; 51 // ImsFeature Status from the ImsService. Cached. 52 protected Integer mFeatureStateCached = null; 53 protected long mFeatureCapabilities; 54 private final IImsRegistration mRegistrationBinder; 55 private final IImsConfig mConfigBinder; 56 private final ISipTransport mSipTransportBinder; 57 protected final Object mLock = new Object(); 58 FeatureConnection(Context context, int slotId, IImsConfig c, IImsRegistration r, ISipTransport s)59 public FeatureConnection(Context context, int slotId, IImsConfig c, IImsRegistration r, 60 ISipTransport s) { 61 mSlotId = slotId; 62 mContext = context; 63 mRegistrationBinder = r; 64 mConfigBinder = c; 65 mSipTransportBinder = s; 66 } 67 getTelephonyManager()68 protected TelephonyManager getTelephonyManager() { 69 return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 70 } 71 72 /** 73 * Set the binder which type is IImsMmTelFeature or IImsRcsFeature to connect to MmTelFeature 74 * or RcsFeature. 75 */ setBinder(IBinder binder)76 public void setBinder(IBinder binder) { 77 synchronized (mLock) { 78 mBinder = binder; 79 try { 80 if (mBinder != null) { 81 mBinder.linkToDeath(mDeathRecipient, 0); 82 } 83 } catch (RemoteException e) { 84 Log.w(TAG, "setBinder: linkToDeath on already dead Binder, setting null"); 85 mBinder = null; 86 } 87 } 88 } 89 90 protected final IBinder.DeathRecipient mDeathRecipient = () -> { 91 Log.w(TAG, "DeathRecipient triggered, binder died."); 92 if (mContext != null && Looper.getMainLooper() != null) { 93 // Move this signal to the main thread, notifying ImsManager of the Binder 94 // death on another thread may lead to deadlocks. 95 mContext.getMainExecutor().execute(this::onRemovedOrDied); 96 return; 97 } 98 // No choice - execute on the current Binder thread. 99 onRemovedOrDied(); 100 }; 101 102 /** 103 * Called when the MmTelFeature/RcsFeature has either been removed by Telephony or crashed. 104 */ onRemovedOrDied()105 protected void onRemovedOrDied() { 106 synchronized (mLock) { 107 if (mIsAvailable) { 108 mIsAvailable = false; 109 try { 110 if (mBinder != null) { 111 mBinder.unlinkToDeath(mDeathRecipient, 0); 112 } 113 } catch (NoSuchElementException e) { 114 Log.w(TAG, "onRemovedOrDied: unlinkToDeath called on unlinked Binder."); 115 } 116 } 117 } 118 } 119 getRegistrationTech()120 public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech() 121 throws RemoteException { 122 IImsRegistration registration = getRegistration(); 123 if (registration != null) { 124 return registration.getRegistrationTechnology(); 125 } else { 126 Log.w(TAG, "getRegistrationTech: ImsRegistration is null"); 127 return ImsRegistrationImplBase.REGISTRATION_TECH_NONE; 128 } 129 } 130 getRegistration()131 public @Nullable IImsRegistration getRegistration() { 132 return mRegistrationBinder; 133 } 134 getConfig()135 public @Nullable IImsConfig getConfig() { 136 return mConfigBinder; 137 } 138 getSipTransport()139 public @Nullable ISipTransport getSipTransport() { 140 return mSipTransportBinder; 141 } 142 143 @VisibleForTesting checkServiceIsReady()144 public void checkServiceIsReady() throws RemoteException { 145 if (!sImsSupportedOnDevice) { 146 throw new RemoteException("IMS is not supported on this device."); 147 } 148 if (!isBinderReady()) { 149 throw new RemoteException("ImsServiceProxy is not ready to accept commands."); 150 } 151 } 152 153 /** 154 * @return Returns true if the ImsService is ready to take commands, false otherwise. If this 155 * method returns false, it doesn't mean that the Binder connection is not available (use 156 * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands 157 * at this time. 158 * 159 * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take 160 * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}. 161 */ isBinderReady()162 public boolean isBinderReady() { 163 return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY; 164 } 165 166 /** 167 * @return false if the binder connection is no longer alive. 168 */ isBinderAlive()169 public boolean isBinderAlive() { 170 return mIsAvailable && mBinder != null && mBinder.isBinderAlive(); 171 } 172 updateFeatureState(int state)173 public void updateFeatureState(int state) { 174 synchronized (mLock) { 175 mFeatureStateCached = state; 176 } 177 } 178 getFeatureCapabilties()179 public long getFeatureCapabilties() { 180 synchronized (mLock) { 181 return mFeatureCapabilities; 182 } 183 } 184 updateFeatureCapabilities(long caps)185 public void updateFeatureCapabilities(long caps) { 186 synchronized (mLock) { 187 if (mFeatureCapabilities != caps) { 188 mFeatureCapabilities = caps; 189 onFeatureCapabilitiesUpdated(caps); 190 } 191 } 192 } 193 isCapable(@msService.ImsServiceCapability long capabilities)194 public boolean isCapable(@ImsService.ImsServiceCapability long capabilities) 195 throws RemoteException { 196 if (!isBinderAlive()) { 197 throw new RemoteException("isCapable: ImsService is not alive"); 198 } 199 return (getFeatureCapabilties() & capabilities) > 0; 200 } 201 202 /** 203 * @return an integer describing the current Feature Status, defined in 204 * {@link ImsFeature.ImsState}. 205 */ getFeatureState()206 public int getFeatureState() { 207 synchronized (mLock) { 208 if (isBinderAlive() && mFeatureStateCached != null) { 209 return mFeatureStateCached; 210 } 211 } 212 // Don't synchronize on Binder call. 213 Integer state = retrieveFeatureState(); 214 synchronized (mLock) { 215 if (state == null) { 216 return ImsFeature.STATE_UNAVAILABLE; 217 } 218 // Cache only non-null value for feature status. 219 mFeatureStateCached = state; 220 } 221 Log.i(TAG + " [" + mSlotId + "]", "getFeatureState - returning " 222 + ImsFeature.STATE_LOG_MAP.get(state)); 223 return state; 224 } 225 226 /** 227 * Internal method used to retrieve the feature status from the corresponding ImsService. 228 */ retrieveFeatureState()229 protected abstract Integer retrieveFeatureState(); 230 onFeatureCapabilitiesUpdated(long capabilities)231 protected abstract void onFeatureCapabilitiesUpdated(long capabilities); 232 } 233