1 /* 2 * Copyright (C) 2020 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 android.telephony.ims.stub; 18 19 import android.annotation.NonNull; 20 import android.annotation.SuppressLint; 21 import android.annotation.SystemApi; 22 import android.os.Binder; 23 import android.os.IBinder; 24 import android.os.RemoteException; 25 import android.telephony.ims.DelegateMessageCallback; 26 import android.telephony.ims.DelegateRequest; 27 import android.telephony.ims.DelegateStateCallback; 28 import android.telephony.ims.SipDelegateManager; 29 import android.telephony.ims.aidl.ISipDelegate; 30 import android.telephony.ims.aidl.ISipDelegateMessageCallback; 31 import android.telephony.ims.aidl.ISipDelegateStateCallback; 32 import android.telephony.ims.aidl.ISipTransport; 33 import android.telephony.ims.aidl.SipDelegateAidlWrapper; 34 import android.util.Log; 35 36 import java.util.ArrayList; 37 import java.util.NoSuchElementException; 38 import java.util.Objects; 39 import java.util.concurrent.Executor; 40 41 /** 42 * The ImsService implements this class to manage the creation and destruction of 43 * {@link SipDelegate}s. 44 * 45 * {@link SipDelegate}s allow the ImsService to forward SIP traffic generated and consumed by IMS 46 * applications as a delegate to the associated carrier's IMS Network in order to support using a 47 * single IMS registration for all MMTEL and RCS signalling traffic. 48 * @hide 49 */ 50 @SystemApi 51 public class SipTransportImplBase { 52 private static final String LOG_TAG = "SipTransportIB"; 53 54 private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { 55 @Override 56 public void binderDied() { 57 // Clean up all binders in this case. 58 mBinderExecutor.execute(() -> binderDiedInternal(null)); 59 } 60 @Override 61 public void binderDied(IBinder who) { 62 mBinderExecutor.execute(() -> binderDiedInternal(who)); 63 } 64 }; 65 66 private final ISipTransport.Stub mSipTransportImpl = new ISipTransport.Stub() { 67 @Override 68 public void createSipDelegate(int subId, DelegateRequest request, 69 ISipDelegateStateCallback dc, ISipDelegateMessageCallback mc) { 70 final long token = Binder.clearCallingIdentity(); 71 try { 72 mBinderExecutor.execute(() -> createSipDelegateInternal(subId, request, dc, mc)); 73 } finally { 74 Binder.restoreCallingIdentity(token); 75 } 76 } 77 78 @Override 79 public void destroySipDelegate(ISipDelegate delegate, int reason) { 80 final long token = Binder.clearCallingIdentity(); 81 try { 82 mBinderExecutor.execute(() -> destroySipDelegateInternal(delegate, reason)); 83 } finally { 84 Binder.restoreCallingIdentity(token); 85 } 86 } 87 }; 88 89 private Executor mBinderExecutor; 90 private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>(); 91 92 /** 93 * Create a new SipTransport. 94 * <p> 95 * Method stubs called from the framework will be called asynchronously. To specify the 96 * {@link Executor} that the methods stubs will be called, use 97 * {@link SipTransportImplBase#SipTransportImplBase(Executor)} instead. 98 */ SipTransportImplBase()99 public SipTransportImplBase() { 100 super(); 101 } 102 103 /** 104 * Create an implementation of SipTransportImplBase. 105 * 106 * @param executor The executor that remote calls from the framework will be called on. This 107 * includes the methods here as well as the methods in {@link SipDelegate}. 108 */ SipTransportImplBase(@onNull Executor executor)109 public SipTransportImplBase(@NonNull Executor executor) { 110 if (executor == null) { 111 throw new IllegalArgumentException("executor must not be null"); 112 } 113 114 mBinderExecutor = executor; 115 } 116 117 /** 118 * Called by the Telephony framework to request the creation of a new {@link SipDelegate}. 119 * <p> 120 * The implementation must call 121 * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} with 122 * the {@link SipDelegate} that is associated with the {@link DelegateRequest}. 123 * <p> 124 * This method will be called on the Executor specified in 125 * {@link SipTransportImplBase#SipTransportImplBase(Executor)}. 126 * 127 * @param subscriptionId The subscription ID associated with the requested {@link SipDelegate}. 128 * @param request A SIP delegate request containing the parameters that the remote RCS 129 * application wishes to use. 130 * @param dc A callback back to the remote application to be used to communicate state callbacks 131 * for the SipDelegate. 132 * @param mc A callback back to the remote application to be used to send SIP messages to the 133 * remote application and acknowledge the sending of outgoing SIP messages. 134 */ 135 // executor used is defined in the constructor. 136 @SuppressLint("ExecutorRegistration") createSipDelegate(int subscriptionId, @NonNull DelegateRequest request, @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc)137 public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request, 138 @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) { 139 throw new UnsupportedOperationException("createSipDelegate not implemented!"); 140 } 141 142 /** 143 * Destroys the SipDelegate associated with a remote IMS application. 144 * <p> 145 * After the delegate is destroyed, {@link DelegateStateCallback#onDestroyed(int)} must be 146 * called to notify listeners of its destruction to release associated resources. 147 * <p> 148 * This method will be called on the Executor specified in 149 * {@link SipTransportImplBase#SipTransportImplBase(Executor)}. 150 * @param delegate The delegate to be destroyed. 151 * @param reason The reason the remote connection to this {@link SipDelegate} is being 152 * destroyed. 153 */ destroySipDelegate(@onNull SipDelegate delegate, @SipDelegateManager.SipDelegateDestroyReason int reason)154 public void destroySipDelegate(@NonNull SipDelegate delegate, 155 @SipDelegateManager.SipDelegateDestroyReason int reason) { 156 throw new UnsupportedOperationException("destroySipDelegate not implemented!"); 157 } 158 createSipDelegateInternal(int subId, DelegateRequest r, ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc)159 private void createSipDelegateInternal(int subId, DelegateRequest r, 160 ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc) { 161 SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc); 162 mDelegates.add(wrapper); 163 linkDeathRecipient(wrapper); 164 createSipDelegate(subId, r, wrapper, wrapper); 165 } 166 destroySipDelegateInternal(ISipDelegate d, int reason)167 private void destroySipDelegateInternal(ISipDelegate d, int reason) { 168 SipDelegateAidlWrapper result = null; 169 for (SipDelegateAidlWrapper w : mDelegates) { 170 if (Objects.equals(d, w.getDelegateBinder())) { 171 result = w; 172 break; 173 } 174 } 175 176 if (result != null) { 177 unlinkDeathRecipient(result); 178 mDelegates.remove(result); 179 destroySipDelegate(result.getDelegate(), reason); 180 } else { 181 Log.w(LOG_TAG, "destroySipDelegateInternal, could not findSipDelegate corresponding to " 182 + d); 183 } 184 } 185 linkDeathRecipient(SipDelegateAidlWrapper w)186 private void linkDeathRecipient(SipDelegateAidlWrapper w) { 187 try { 188 w.getStateCallbackBinder().asBinder().linkToDeath(mDeathRecipient, 0); 189 } catch (RemoteException e) { 190 Log.w(LOG_TAG, "linkDeathRecipient, remote process already died, cleaning up."); 191 mDeathRecipient.binderDied(w.getStateCallbackBinder().asBinder()); 192 } 193 } 194 unlinkDeathRecipient(SipDelegateAidlWrapper w)195 private void unlinkDeathRecipient(SipDelegateAidlWrapper w) { 196 try { 197 w.getStateCallbackBinder().asBinder().unlinkToDeath(mDeathRecipient, 0); 198 } catch (NoSuchElementException e) { 199 // Ignore this case. 200 } 201 } 202 binderDiedInternal(IBinder who)203 private void binderDiedInternal(IBinder who) { 204 for (SipDelegateAidlWrapper w : mDelegates) { 205 // If the binder itself was not given from the platform, just clean up all binders. 206 if (who == null || w.getStateCallbackBinder().asBinder().equals(who)) { 207 Log.w(LOG_TAG, "Binder death detected for " + w + ", calling destroy and " 208 + "removing."); 209 mDelegates.remove(w); 210 destroySipDelegate(w.getDelegate(), 211 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD); 212 return; 213 } 214 } 215 Log.w(LOG_TAG, "Binder death detected for IBinder " + who + ", but couldn't find matching " 216 + "SipDelegate"); 217 } 218 219 /** 220 * @return The IInterface used by the framework. 221 * @hide 222 */ getBinder()223 public ISipTransport getBinder() { 224 return mSipTransportImpl; 225 } 226 227 /** 228 * Set default Executor from ImsService. 229 * @param executor The default executor for the framework to use when executing the methods 230 * overridden by the implementation of SipTransport. 231 * @hide 232 */ setDefaultExecutor(@onNull Executor executor)233 public final void setDefaultExecutor(@NonNull Executor executor) { 234 if (mBinderExecutor == null) { 235 mBinderExecutor = executor; 236 } 237 } 238 } 239