1 /**
2  * Copyright (C) 2022 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.telephony.imsmedia;
18 
19 import android.app.Service;
20 import android.content.Intent;
21 import android.os.IBinder;
22 import android.os.Parcel;
23 import android.os.ParcelFileDescriptor;
24 import android.support.annotation.GuardedBy;
25 import android.telephony.imsmedia.IImsAudioSession;
26 import android.telephony.imsmedia.IImsAudioSessionCallback;
27 import android.telephony.imsmedia.IImsMedia;
28 import android.telephony.imsmedia.IImsMediaCallback;
29 import android.telephony.imsmedia.IImsTextSession;
30 import android.telephony.imsmedia.IImsTextSessionCallback;
31 import android.telephony.imsmedia.IImsVideoSession;
32 import android.telephony.imsmedia.IImsVideoSessionCallback;
33 import android.telephony.imsmedia.ImsMediaSession;
34 import android.telephony.imsmedia.RtpConfig;
35 import android.telephony.imsmedia.VideoConfig;
36 import android.util.SparseArray;
37 
38 import com.android.internal.util.DumpUtils;
39 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
40 import com.android.telephony.imsmedia.util.Log;
41 
42 import java.io.FileDescriptor;
43 import java.io.PrintWriter;
44 import java.util.concurrent.atomic.AtomicInteger;
45 
46 /** Controller that maintains all IMS Media sessions */
47 public class ImsMediaController extends Service {
48 
49     private static final String TAG = "ImsMediaController";
50     private AtomicInteger mSessionId = new AtomicInteger();
51     private OpenSessionCallback mCallback = new OpenSessionCallback();
52 
53     @GuardedBy("mSessions")
54     private final SparseArray<IMediaSession> mSessions = new SparseArray();
55 
56     private class ImsMediaBinder extends IImsMedia.Stub {
57         // TODO add permission checks
58         @Override
openSession( final ParcelFileDescriptor rtpFd, final ParcelFileDescriptor rtcpFd, final int sessionType, final RtpConfig rtpConfig, final IBinder callback)59         public void openSession(
60             final ParcelFileDescriptor rtpFd, final ParcelFileDescriptor rtcpFd,
61             final int sessionType, final RtpConfig rtpConfig, final IBinder callback) {
62             final int sessionId = mSessionId.getAndIncrement();
63 
64             IMediaSession session;
65             Log.d(TAG, "openSession: sessionId = " + sessionId
66                     + ", type=" + sessionType + "," + Log.hidePii(String.valueOf(rtpConfig)));
67             synchronized (mSessions) {
68                 switch (sessionType) {
69                     case ImsMediaSession.SESSION_TYPE_AUDIO:
70                         session = new AudioSession(sessionId,
71                                 IImsAudioSessionCallback.Stub.asInterface(callback));
72                         break;
73                     case ImsMediaSession.SESSION_TYPE_VIDEO:
74                         JNIImsMediaService.setAssetManager(ImsMediaController.this.getAssets());
75                         session = new VideoSession(sessionId,
76                                 IImsVideoSessionCallback.Stub.asInterface(callback));
77                         break;
78                     case ImsMediaSession.SESSION_TYPE_RTT:
79                         session = new TextSession(sessionId,
80                                 IImsTextSessionCallback.Stub.asInterface(callback));
81                         break;
82                     default:
83                         session = null;
84                 }
85 
86                 if (session != null) {
87                     mSessions.append(sessionId, session);
88                     session.openSession(new OpenSessionParams(rtpFd, rtcpFd, rtpConfig,
89                         mCallback));
90 
91                 }
92             }
93         }
94 
95         @Override
closeSession(IBinder session)96         public void closeSession(IBinder session) {
97             Log.d(TAG, "closeSession: " + session);
98             synchronized (mSessions) {
99                 if (session instanceof AudioSession) {
100                     final AudioSession audioSession =
101                             (AudioSession) IImsAudioSession.Stub.asInterface(session);
102                     audioSession.closeSession();
103                 } else if (session instanceof VideoSession) {
104                     final VideoSession videoSession =
105                             (VideoSession) IImsVideoSession.Stub.asInterface(session);
106                     videoSession.closeSession();
107                 } else if (session instanceof TextSession) {
108                     final TextSession textSession =
109                             (TextSession) IImsTextSession.Stub.asInterface(session);
110                     textSession.closeSession();
111                 }
112             }
113         }
114 
115         @Override
generateVideoSprop(VideoConfig[] videoConfigList, IBinder callback)116         public void generateVideoSprop(VideoConfig[] videoConfigList, IBinder callback) {
117 
118             if (videoConfigList == null || callback == null) {
119                 Log.w(TAG, "[SPROP] Invalid params");
120                 return;
121             }
122 
123             try {
124                 int len = videoConfigList.length;
125                 String[] spropList = new String[len];
126 
127                 int idx = 0;
128                 for (VideoConfig config : videoConfigList) {
129                     Parcel parcel = Parcel.obtain();
130                     config.writeToParcel(parcel, 0);
131                     parcel.setDataPosition(0);
132                     spropList[idx] = JNIImsMediaService.generateSprop(parcel.marshall());
133                     parcel.recycle();
134                     idx++;
135                 }
136                 IImsMediaCallback.Stub.asInterface(callback).onVideoSpropResponse(spropList);
137             } catch (Exception e) {
138                 Log.e(TAG, "[SPROP] Error: " + e.toString());
139             }
140         }
141     }
142 
143     private IImsMedia.Stub mImsMediaBinder = new ImsMediaBinder();
144 
145     @Override
onBind(Intent intent)146     public IBinder onBind(Intent intent) {
147         Log.d(TAG, Thread.currentThread().getName() + " onBind");
148         return mImsMediaBinder;
149     }
150 
151     @Override
onUnbind(Intent intent)152     public boolean onUnbind(Intent intent) {
153         Log.d(TAG, Thread.currentThread().getName() + " onUnbind");
154         try {
155             synchronized (mSessions) {
156                 while (mSessions.size() > 0) {
157                     mImsMediaBinder.closeSession((IBinder) mSessions.valueAt(0));
158                     mSessions.removeAt(0);
159                 }
160                 JNIImsMediaService.clearListener();
161                 mSessions.clear();
162             }
163         } catch (Exception e) {
164             Log.e(TAG, "onUnbind: e = " + e);
165         }
166         return true;
167     }
168 
169     @Override
onCreate()170     public void onCreate() {
171         Log.d(TAG, "onCreate");
172     }
173 
174     @Override
onDestroy()175     public void onDestroy() {
176         Log.d(TAG, "onDestroy");
177         // Release all wakelocks (if leaked)
178         WakeLockManager.getInstance().cleanup();
179     }
180 
181     @Override
dump(FileDescriptor fd, PrintWriter writer, String[] args)182     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
183         if (!DumpUtils.checkDumpAndUsageStatsPermission(this, TAG, writer)) return;
184 
185         writer.println("===== ImsMedia Service dump =====");
186 
187         writer.println("\n--- ImsMediaController ---\n");
188         synchronized (mSessions) {
189             writer.println("Session List: \n\tCount: " + mSessions.size());
190             for (int i = 0; i < mSessions.size(); i++) {
191                 int sessionId = mSessions.keyAt(i);
192                 writer.println("\tSession Id: " + sessionId
193                         + "\tObj:" + mSessions.get(sessionId));
194             }
195         }
196         writer.println("\n--- END: ImsMediaController ---\n");
197 
198         WakeLockManager.getInstance().dump(writer);
199 
200         writer.println("===== END : ImsMedia Service dump =====");
201     }
202 
getSession(int sessionId)203     private IMediaSession getSession(int sessionId) {
204         synchronized (mSessions) {
205             return mSessions.get(sessionId);
206         }
207     }
208 
209     public class OpenSessionCallback {
onOpenSessionSuccess(int sessionId, Object session)210         public void onOpenSessionSuccess(int sessionId, Object session) {
211             Log.d(TAG, "onOpenSessionSuccess: sessionId = " + sessionId);
212             getSession(sessionId).onOpenSessionSuccess(session);
213         }
214 
onOpenSessionFailure(int sessionId, int error)215         public void onOpenSessionFailure(int sessionId, int error) {
216             synchronized (mSessions) {
217                 Log.e(TAG, "onOpenSessionFailure: sessionId = " + sessionId + " error = " + error);
218                 getSession(sessionId).onOpenSessionFailure(error);
219                 mSessions.remove(sessionId);
220             }
221         }
222 
223         /**
224          * Called when the session is closed.
225          *
226          * @param sessionId identifier of the session
227          */
onSessionClosed(int sessionId)228         public void onSessionClosed(int sessionId) {
229             synchronized (mSessions) {
230                 Log.d(TAG, "onSessionClosed: sessionId = " + sessionId);
231                 getSession(sessionId).onSessionClosed();
232                 mSessions.remove(sessionId);
233 
234                 if (mSessions.size() <= 0) {
235                     WakeLockManager.getInstance().cleanup();
236                 }
237             }
238         }
239     }
240 }
241