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.Log; 37 import android.util.SparseArray; 38 39 import com.android.telephony.imsmedia.Utils.OpenSessionParams; 40 41 import java.util.concurrent.atomic.AtomicInteger; 42 43 /** Controller that maintains all IMS Media sessions */ 44 public class ImsMediaController extends Service { 45 46 private static final String TAG = "ImsMediaController"; 47 private AtomicInteger mSessionId = new AtomicInteger(); 48 private OpenSessionCallback mCallback = new OpenSessionCallback(); 49 50 @GuardedBy("mSessions") 51 private final SparseArray<IMediaSession> mSessions = new SparseArray(); 52 53 private class ImsMediaBinder extends IImsMedia.Stub { 54 // TODO add permission checks 55 @Override openSession( final ParcelFileDescriptor rtpFd, final ParcelFileDescriptor rtcpFd, final int sessionType, final RtpConfig rtpConfig, final IBinder callback)56 public void openSession( 57 final ParcelFileDescriptor rtpFd, final ParcelFileDescriptor rtcpFd, 58 final int sessionType, final RtpConfig rtpConfig, final IBinder callback) { 59 final int sessionId = mSessionId.getAndIncrement(); 60 61 IMediaSession session; 62 Log.d(TAG, "openSession: sessionId = " + sessionId 63 + ", type=" + sessionType + "," + rtpConfig); 64 synchronized (mSessions) { 65 switch (sessionType) { 66 case ImsMediaSession.SESSION_TYPE_AUDIO: 67 session = new AudioSession(sessionId, 68 IImsAudioSessionCallback.Stub.asInterface(callback)); 69 break; 70 case ImsMediaSession.SESSION_TYPE_VIDEO: 71 JNIImsMediaService.setAssetManager(ImsMediaController.this.getAssets()); 72 session = new VideoSession(sessionId, 73 IImsVideoSessionCallback.Stub.asInterface(callback)); 74 break; 75 case ImsMediaSession.SESSION_TYPE_RTT: 76 session = new TextSession(sessionId, 77 IImsTextSessionCallback.Stub.asInterface(callback)); 78 break; 79 default: 80 session = null; 81 } 82 83 if (session != null) { 84 mSessions.append(sessionId, session); 85 session.openSession(new OpenSessionParams(rtpFd, rtcpFd, rtpConfig, 86 mCallback)); 87 88 } 89 } 90 } 91 92 @Override closeSession(IBinder session)93 public void closeSession(IBinder session) { 94 Log.d(TAG, "closeSession: " + session); 95 synchronized (mSessions) { 96 if (session instanceof AudioSession) { 97 final AudioSession audioSession = 98 (AudioSession) IImsAudioSession.Stub.asInterface(session); 99 audioSession.closeSession(); 100 } else if (session instanceof VideoSession) { 101 final VideoSession videoSession = 102 (VideoSession) IImsVideoSession.Stub.asInterface(session); 103 videoSession.closeSession(); 104 } else if (session instanceof TextSession) { 105 final TextSession textSession = 106 (TextSession) IImsTextSession.Stub.asInterface(session); 107 textSession.closeSession(); 108 } 109 } 110 } 111 112 @Override generateVideoSprop(VideoConfig[] videoConfigList, IBinder callback)113 public void generateVideoSprop(VideoConfig[] videoConfigList, IBinder callback) { 114 115 if (videoConfigList == null || callback == null) { 116 Log.d(TAG, "[SPROP] Invalid params"); 117 return; 118 } 119 120 try { 121 int len = videoConfigList.length; 122 String[] spropList = new String[len]; 123 124 int idx = 0; 125 for (VideoConfig config : videoConfigList) { 126 Parcel parcel = Parcel.obtain(); 127 config.writeToParcel(parcel, 0); 128 parcel.setDataPosition(0); 129 spropList[idx] = JNIImsMediaService.generateSprop(parcel.marshall()); 130 parcel.recycle(); 131 idx++; 132 } 133 IImsMediaCallback.Stub.asInterface(callback).onVideoSpropResponse(spropList); 134 } catch (Exception e) { 135 Log.e(TAG, "[SPROP] Error: " + e.toString()); 136 } 137 } 138 } 139 140 private IImsMedia.Stub mImsMediaBinder = new ImsMediaBinder(); 141 142 @Override onBind(Intent intent)143 public IBinder onBind(Intent intent) { 144 Log.d(TAG, Thread.currentThread().getName() + " onBind"); 145 return mImsMediaBinder; 146 } 147 148 @Override onUnbind(Intent intent)149 public boolean onUnbind(Intent intent) { 150 Log.d(TAG, Thread.currentThread().getName() + " onUnbind"); 151 try { 152 synchronized (mSessions) { 153 while (mSessions.size() > 0) { 154 mImsMediaBinder.closeSession((IBinder) mSessions.valueAt(0)); 155 mSessions.removeAt(0); 156 } 157 JNIImsMediaService.clearListener(); 158 mSessions.clear(); 159 } 160 } catch (Exception e) { 161 Log.d(TAG, "onUnbind: e=" + e); 162 } 163 return true; 164 } 165 166 @Override onCreate()167 public void onCreate() { 168 Log.d(TAG, "onCreate"); 169 } 170 171 @Override onDestroy()172 public void onDestroy() { 173 Log.d(TAG, "onDestroy"); 174 } 175 getSession(int sessionId)176 private IMediaSession getSession(int sessionId) { 177 synchronized (mSessions) { 178 return mSessions.get(sessionId); 179 } 180 } 181 182 public class OpenSessionCallback { onOpenSessionSuccess(int sessionId, Object session)183 public void onOpenSessionSuccess(int sessionId, Object session) { 184 getSession(sessionId).onOpenSessionSuccess(session); 185 } 186 onOpenSessionFailure(int sessionId, int error)187 public void onOpenSessionFailure(int sessionId, int error) { 188 synchronized (mSessions) { 189 getSession(sessionId).onOpenSessionFailure(error); 190 mSessions.remove(sessionId); 191 } 192 } 193 194 /** 195 * Called when the session is closed. 196 * 197 * @param sessionId identifier of the session 198 */ onSessionClosed(int sessionId)199 public void onSessionClosed(int sessionId) { 200 synchronized (mSessions) { 201 getSession(sessionId).onSessionClosed(); 202 Log.d(TAG, "onSessionClosed: sessionId = " + sessionId); 203 mSessions.remove(sessionId); 204 } 205 } 206 } 207 } 208