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