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