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