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 final Executor mBinderExecutor;
90     private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>();
91 
92     /**
93      * Create an implementation of SipTransportImplBase.
94      *
95      * @param executor The executor that remote calls from the framework will be called on. This
96      *                 includes the methods here as well as the methods in {@link SipDelegate}.
97      */
SipTransportImplBase(@onNull Executor executor)98     public SipTransportImplBase(@NonNull Executor executor) {
99         if (executor == null) {
100             throw new IllegalArgumentException("executor must not be null");
101         }
102 
103         mBinderExecutor = executor;
104     }
105 
106     /**
107      * Called by the Telephony framework to request the creation of a new {@link SipDelegate}.
108      * <p>
109      * The implementation must call
110      * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} with
111      * the {@link SipDelegate} that is associated with the {@link DelegateRequest}.
112      * <p>
113      * This method will be called on the Executor specified in
114      * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
115      *
116      * @param subscriptionId The subscription ID associated with the requested {@link SipDelegate}.
117      * @param request A SIP delegate request containing the parameters that the remote RCS
118      * application wishes to use.
119      * @param dc A callback back to the remote application to be used to communicate state callbacks
120      *           for the SipDelegate.
121      * @param mc A callback back to the remote application to be used to send SIP messages to the
122      *           remote application and acknowledge the sending of outgoing SIP messages.
123      */
124     // executor used is defined in the constructor.
125     @SuppressLint("ExecutorRegistration")
createSipDelegate(int subscriptionId, @NonNull DelegateRequest request, @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc)126     public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request,
127             @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) {
128         throw new UnsupportedOperationException("createSipDelegate not implemented!");
129     }
130 
131     /**
132      * Destroys the SipDelegate associated with a remote IMS application.
133      * <p>
134      * After the delegate is destroyed, {@link DelegateStateCallback#onDestroyed(int)} must be
135      * called to notify listeners of its destruction to release associated resources.
136      * <p>
137      * This method will be called on the Executor specified in
138      * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
139      * @param delegate The delegate to be destroyed.
140      * @param reason The reason the remote connection to this {@link SipDelegate} is being
141      *         destroyed.
142      */
destroySipDelegate(@onNull SipDelegate delegate, @SipDelegateManager.SipDelegateDestroyReason int reason)143     public void destroySipDelegate(@NonNull SipDelegate delegate,
144             @SipDelegateManager.SipDelegateDestroyReason int reason) {
145         throw new UnsupportedOperationException("destroySipDelegate not implemented!");
146     }
147 
createSipDelegateInternal(int subId, DelegateRequest r, ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc)148     private void createSipDelegateInternal(int subId, DelegateRequest r,
149             ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc) {
150         SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc);
151         mDelegates.add(wrapper);
152         linkDeathRecipient(wrapper);
153         createSipDelegate(subId, r, wrapper, wrapper);
154     }
155 
destroySipDelegateInternal(ISipDelegate d, int reason)156     private void destroySipDelegateInternal(ISipDelegate d, int reason) {
157         SipDelegateAidlWrapper result = null;
158         for (SipDelegateAidlWrapper w : mDelegates) {
159             if (Objects.equals(d, w.getDelegateBinder())) {
160                 result = w;
161                 break;
162             }
163         }
164 
165         if (result != null) {
166             unlinkDeathRecipient(result);
167             mDelegates.remove(result);
168             destroySipDelegate(result.getDelegate(), reason);
169         } else {
170             Log.w(LOG_TAG, "destroySipDelegateInternal, could not findSipDelegate corresponding to "
171                     + d);
172         }
173     }
174 
linkDeathRecipient(SipDelegateAidlWrapper w)175     private void linkDeathRecipient(SipDelegateAidlWrapper w) {
176         try {
177             w.getStateCallbackBinder().asBinder().linkToDeath(mDeathRecipient, 0);
178         } catch (RemoteException e) {
179             Log.w(LOG_TAG, "linkDeathRecipient, remote process already died, cleaning up.");
180             mDeathRecipient.binderDied(w.getStateCallbackBinder().asBinder());
181         }
182     }
183 
unlinkDeathRecipient(SipDelegateAidlWrapper w)184     private void unlinkDeathRecipient(SipDelegateAidlWrapper w) {
185         try {
186             w.getStateCallbackBinder().asBinder().unlinkToDeath(mDeathRecipient, 0);
187         } catch (NoSuchElementException e) {
188             // Ignore this case.
189         }
190     }
191 
binderDiedInternal(IBinder who)192     private void binderDiedInternal(IBinder who) {
193         for (SipDelegateAidlWrapper w : mDelegates) {
194             // If the binder itself was not given from the platform, just clean up all binders.
195             if (who == null || w.getStateCallbackBinder().asBinder().equals(who))  {
196                 Log.w(LOG_TAG, "Binder death detected for " + w + ", calling destroy and "
197                         + "removing.");
198                 mDelegates.remove(w);
199                 destroySipDelegate(w.getDelegate(),
200                         SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
201                 return;
202             }
203         }
204         Log.w(LOG_TAG, "Binder death detected for IBinder " + who + ", but couldn't find matching "
205                 + "SipDelegate");
206     }
207 
208     /**
209      * @return The IInterface used by the framework.
210      * @hide
211      */
getBinder()212     public ISipTransport getBinder() {
213         return mSipTransportImpl;
214     }
215 }
216