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 android.telephony.imsmedia; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.os.IBinder; 24 import android.os.ParcelFileDescriptor; 25 import android.os.RemoteException; 26 import android.util.Log; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 import androidx.annotation.VisibleForTesting; 31 32 import java.net.DatagramSocket; 33 import java.util.Objects; 34 import java.util.concurrent.Executor; 35 36 37 /** 38 * IMS media manager APIs to manage the IMS media sessions 39 * 40 * @hide 41 */ 42 public class ImsMediaManager { 43 private static final String TAG = "ImsMediaManager"; 44 @VisibleForTesting 45 protected static final String MEDIA_SERVICE_PACKAGE = "com.android.telephony.imsmedia"; 46 @VisibleForTesting 47 protected static final String MEDIA_SERVICE_CLASS = 48 MEDIA_SERVICE_PACKAGE + ".ImsMediaController"; 49 private final Context mContext; 50 private final OnConnectedCallback mOnConnectedCallback; 51 private final Executor mExecutor; 52 private final ServiceConnection mConnection; 53 private volatile IImsMedia mImsMedia; 54 55 /** 56 * Opens a RTP session based on the local sockets with the associated initial 57 * remote configuration if there is a valid {@link RtpConfig} passed. It starts 58 * the media flow if the media direction in the {@link RtpConfig} is set to any 59 * value other than {@link RtpConfig#NO_MEDIA_FLOW}. If the open session is 60 * successful then a new {@link ImsMediaSession} object will be returned using 61 * the {@link SessionCallback#onOpenSessionSuccess(ImsMediaSession)} API. If the 62 * open session is failed then an error code {@link SessionOperationResult} will 63 * be returned using {@link SessionCallback#onOpenSessionFailure(int)} API. 64 * 65 * @param rtpSocket local UDP socket to send and receive incoming RTP packets 66 * @param rtcpSocket local UDP socket to send and receive incoming RTCP packets 67 * @param rtpConfig provides remote endpoint info and codec details. 68 * This could be null initially and the application may update 69 * this later using {@link ImsMediaSession#modifySession()} 70 * API. 71 * @param callback callbacks to receive session specific notifications. 72 */ openSession(@onNull final DatagramSocket rtpSocket, @NonNull final DatagramSocket rtcpSocket, @NonNull final @ImsMediaSession.SessionType int sessionType, @Nullable final RtpConfig rtpConfig, @NonNull final Executor executor, @NonNull final SessionCallback callback)73 public void openSession(@NonNull final DatagramSocket rtpSocket, 74 @NonNull final DatagramSocket rtcpSocket, 75 @NonNull final @ImsMediaSession.SessionType int sessionType, 76 @Nullable final RtpConfig rtpConfig, 77 @NonNull final Executor executor, 78 @NonNull final SessionCallback callback) { 79 if (isConnected()) { 80 try { 81 callback.setExecutor(executor); 82 mImsMedia.openSession(ParcelFileDescriptor.fromDatagramSocket(rtpSocket), 83 ParcelFileDescriptor.fromDatagramSocket(rtcpSocket), sessionType, 84 rtpConfig, callback.getBinder()); 85 } catch (RemoteException e) { 86 Log.e(TAG, "Failed to openSession: " + e); 87 // TODO throw exception or callback.onOpenSessionFailure() 88 } 89 } 90 } 91 92 /** 93 * Closes the RTP session including cleanup of all the resources 94 * associated with the session. This will also close the session object 95 * and associated callback. 96 * 97 * @param session RTP session to be closed. 98 */ closeSession(@onNull ImsMediaSession session)99 public void closeSession(@NonNull ImsMediaSession session) { 100 if (isConnected()) { 101 try { 102 mImsMedia.closeSession(session.getBinder()); 103 } catch (RemoteException e) { 104 Log.e(TAG, "Failed to closeSession: " + e); 105 } 106 } 107 } 108 109 /** 110 * Generates the array of SPROP strings for the given array of video 111 * configurations and returns via IImsMediaCallback. 112 * 113 * @param videoConfigList array of video configuration for which sprop should be generated. 114 * @param callback Binder interface implemented by caller and called with array of generated 115 * sprop values. 116 **/ generateVideoSprop(@onNull VideoConfig[] videoConfigList, IBinder callback)117 public void generateVideoSprop(@NonNull VideoConfig[] videoConfigList, IBinder callback) { 118 if (isConnected()) { 119 try { 120 mImsMedia.generateVideoSprop(videoConfigList, callback); 121 } catch (RemoteException e) { 122 Log.e(TAG, "Failed to closeSession: " + e); 123 } 124 } 125 } 126 127 /** 128 * Unbinds the service connection with ImsMediaService 129 */ release()130 public void release() { 131 // TODO: close all the open sessions 132 if (isConnected()) { 133 try { 134 mContext.unbindService(mConnection); 135 } catch (IllegalArgumentException e) { 136 Log.e(TAG, "IllegalArgumentException: " + e.toString()); 137 } 138 mImsMedia = null; 139 } 140 } 141 142 /** 143 * Interface to send call-backs to the application when the service is 144 * connected. 145 */ 146 public interface OnConnectedCallback { 147 /** 148 * Called by the ImsMedia framework when the service is connected. 149 */ onConnected()150 void onConnected(); 151 152 /** 153 * Called by the ImsMedia framework when the service is disconnected. 154 */ onDisconnected()155 void onDisconnected(); 156 } 157 ImsMediaManager(@onNull Context context, @NonNull Executor executor, @NonNull OnConnectedCallback callback)158 public ImsMediaManager(@NonNull Context context, @NonNull Executor executor, 159 @NonNull OnConnectedCallback callback) { 160 161 mContext = Objects.requireNonNull(context, "context cannot be null"); 162 mExecutor = Objects.requireNonNull(executor, "executor cannot be null"); 163 mOnConnectedCallback = Objects.requireNonNull(callback, "callback cannot be null"); 164 165 mConnection = new ServiceConnection() { 166 167 public synchronized void onServiceConnected( 168 ComponentName className, IBinder service) { 169 170 mImsMedia = IImsMedia.Stub.asInterface(service); 171 Log.d(TAG, "onServiceConnected"); 172 mExecutor.execute(new Runnable() { 173 @Override 174 public void run() { 175 mOnConnectedCallback.onConnected(); 176 } 177 }); 178 } 179 180 public void onServiceDisconnected(ComponentName className) { 181 Log.d(TAG, "onServiceDisconnected"); 182 mImsMedia = null; 183 mExecutor.execute(new Runnable() { 184 @Override 185 public void run() { 186 mOnConnectedCallback.onDisconnected(); 187 } 188 }); 189 } 190 }; 191 192 Intent intent = new Intent(IImsMedia.class.getName()); 193 intent.setClassName(MEDIA_SERVICE_PACKAGE, MEDIA_SERVICE_CLASS); 194 boolean bindingSuccessful = mContext.bindService(intent, mConnection, 195 Context.BIND_AUTO_CREATE); 196 197 Log.d(TAG, "binding: " + bindingSuccessful); 198 if (bindingSuccessful) { 199 Log.d(TAG, "bindService successful"); 200 } 201 } 202 isConnected()203 private boolean isConnected() { 204 return mImsMedia != null; 205 } 206 207 public static abstract class SessionCallback { 208 /** @hide */ getBinder()209 public IBinder getBinder() { 210 // Base Implementation 211 return null; 212 } 213 214 /** @hide */ setExecutor(Executor executor)215 public void setExecutor(Executor executor) { 216 // Base Implementation 217 } 218 219 /** 220 * Called when the session is opened successfully 221 * 222 * @param session session object 223 */ onOpenSessionSuccess(ImsMediaSession session)224 public void onOpenSessionSuccess(ImsMediaSession session) { 225 // Base Implementation 226 } 227 228 /** 229 * Called when the open session fails 230 * 231 * @param error Error code 232 */ onOpenSessionFailure(int error)233 public void onOpenSessionFailure(int error) { 234 // Base Implementation 235 } 236 237 /** 238 * Called when the session is closed. 239 */ onSessionClosed()240 public void onSessionClosed() { 241 // Base Implementation 242 } 243 244 } 245 } 246