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