blob: a8744361b23bd490bb27894585999ec4a9da9cbf [file] [log] [blame]
Brad Ebinger89fb5642017-11-06 15:17:32 -08001/*
2 * Copyright (C) 2017 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
17package com.android.ims;
18
Brad Ebinger0e170c72017-12-14 14:24:02 -080019import android.annotation.Nullable;
Brad Ebinger89fb5642017-11-06 15:17:32 -080020import android.app.PendingIntent;
Brad Ebinger0e170c72017-12-14 14:24:02 -080021import android.content.Context;
Brad Ebinger89fb5642017-11-06 15:17:32 -080022import android.os.IBinder;
23import android.os.Message;
24import android.os.RemoteException;
Brad Ebinger0e170c72017-12-14 14:24:02 -080025import android.telephony.Rlog;
26import android.telephony.TelephonyManager;
Brad Ebinger8409c972018-01-11 10:29:04 -080027import android.telephony.ims.aidl.IImsCallSessionListener;
Brad Ebinger89fb5642017-11-06 15:17:32 -080028import android.telephony.ims.feature.ImsFeature;
Mohamed Abdalkaderf8800da2018-01-10 13:29:26 -080029import android.telephony.SmsMessage;
Mohamed Abdalkader38f9d402018-01-22 16:03:21 -080030import android.telephony.ims.internal.stub.SmsImplBase;
Brad Ebinger89fb5642017-11-06 15:17:32 -080031import android.util.Log;
32
33import com.android.ims.internal.IImsCallSession;
Brad Ebinger89fb5642017-11-06 15:17:32 -080034import com.android.ims.internal.IImsConfig;
35import com.android.ims.internal.IImsEcbm;
Brad Ebingerf3cda822017-11-09 10:25:46 -080036import com.android.ims.internal.IImsMMTelFeature;
Brad Ebinger89fb5642017-11-06 15:17:32 -080037import com.android.ims.internal.IImsMultiEndpoint;
Brad Ebinger0e170c72017-12-14 14:24:02 -080038import com.android.ims.internal.IImsRegistration;
Brad Ebinger89fb5642017-11-06 15:17:32 -080039import com.android.ims.internal.IImsRegistrationListener;
Brad Ebingerf3cda822017-11-09 10:25:46 -080040import com.android.ims.internal.IImsServiceFeatureCallback;
Mohamed Abdalkaderf8800da2018-01-10 13:29:26 -080041import com.android.ims.internal.IImsSmsListener;
Brad Ebinger89fb5642017-11-06 15:17:32 -080042import com.android.ims.internal.IImsUt;
43
44/**
45 * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
46 * the platform currently supports: MMTel and RCS.
47 * @hide
48 */
49
Brad Ebinger3c7f2f42017-11-07 11:08:11 -080050public class ImsServiceProxy {
Brad Ebinger89fb5642017-11-06 15:17:32 -080051
Brad Ebinger0e170c72017-12-14 14:24:02 -080052 protected static final String TAG = "ImsServiceProxy";
Brad Ebinger89fb5642017-11-06 15:17:32 -080053 protected final int mSlotId;
54 protected IBinder mBinder;
55 private final int mSupportedFeature;
Brad Ebinger0e170c72017-12-14 14:24:02 -080056 private Context mContext;
Brad Ebinger89fb5642017-11-06 15:17:32 -080057
58 // Start by assuming the proxy is available for usage.
59 private boolean mIsAvailable = true;
60 // ImsFeature Status from the ImsService. Cached.
61 private Integer mFeatureStatusCached = null;
Brad Ebinger0e170c72017-12-14 14:24:02 -080062 private IFeatureUpdate mStatusCallback;
Brad Ebinger89fb5642017-11-06 15:17:32 -080063 private final Object mLock = new Object();
64
Brad Ebinger0e170c72017-12-14 14:24:02 -080065 public static ImsServiceProxy create(Context context , int slotId) {
66 ImsServiceProxy serviceProxy = new ImsServiceProxy(context, slotId, ImsFeature.MMTEL);
67
68 TelephonyManager tm = getTelephonyManager(context);
69 if (tm == null) {
70 Rlog.w(TAG, "getServiceProxy: TelephonyManager is null!");
71 // Binder can be unset in this case because it will be torn down/recreated as part of
72 // a retry mechanism until the serviceProxy binder is set successfully.
73 return serviceProxy;
74 }
75
76 IImsMMTelFeature binder = tm.getImsMMTelFeatureAndListen(slotId,
77 serviceProxy.getListener());
78 if (binder != null) {
79 serviceProxy.setBinder(binder.asBinder());
80 // Trigger the cache to be updated for feature status.
81 serviceProxy.getFeatureStatus();
82 } else {
83 Rlog.w(TAG, "getServiceProxy: binder is null! Phone Id: " + slotId);
84 }
85 return serviceProxy;
86 }
87
88 public static TelephonyManager getTelephonyManager(Context context) {
89 return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
90 }
91
92 public interface IFeatureUpdate {
93 /**
94 * Called when the ImsFeature has changed its state. Use
95 * {@link ImsFeature#getFeatureState()} to get the new state.
96 */
97 void notifyStateChanged();
98
99 /**
100 * Called when the ImsFeature has become unavailable due to the binder switching or app
101 * crashing. A new ImsServiceProxy should be requested for that feature.
102 */
103 void notifyUnavailable();
Brad Ebinger89fb5642017-11-06 15:17:32 -0800104 }
105
Brad Ebingerf3cda822017-11-09 10:25:46 -0800106 private final IImsServiceFeatureCallback mListenerBinder =
107 new IImsServiceFeatureCallback.Stub() {
Brad Ebinger89fb5642017-11-06 15:17:32 -0800108
109 @Override
110 public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
111 // The feature has been re-enabled. This may happen when the service crashes.
112 synchronized (mLock) {
113 if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
Brad Ebinger0e170c72017-12-14 14:24:02 -0800114 Log.i(TAG, "Feature enabled on slotId: " + slotId + " for feature: " +
Brad Ebinger89fb5642017-11-06 15:17:32 -0800115 feature);
116 mIsAvailable = true;
117 }
118 }
119 }
120
121 @Override
122 public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
123 synchronized (mLock) {
124 if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
Brad Ebinger0e170c72017-12-14 14:24:02 -0800125 Log.i(TAG, "Feature disabled on slotId: " + slotId + " for feature: " +
Brad Ebinger89fb5642017-11-06 15:17:32 -0800126 feature);
127 mIsAvailable = false;
Brad Ebinger0e170c72017-12-14 14:24:02 -0800128 if (mStatusCallback != null) {
129 mStatusCallback.notifyUnavailable();
130 }
Brad Ebinger89fb5642017-11-06 15:17:32 -0800131 }
132 }
133 }
134
135 @Override
136 public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
137 synchronized (mLock) {
Brad Ebinger0e170c72017-12-14 14:24:02 -0800138 Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
Brad Ebinger89fb5642017-11-06 15:17:32 -0800139 " status: " + status);
140 if (mSlotId == slotId && feature == mSupportedFeature) {
141 mFeatureStatusCached = status;
142 if (mStatusCallback != null) {
Brad Ebinger0e170c72017-12-14 14:24:02 -0800143 mStatusCallback.notifyStateChanged();
Brad Ebinger89fb5642017-11-06 15:17:32 -0800144 }
145 }
146 }
147 }
148 };
149
Brad Ebinger3418c132018-01-18 12:03:33 -0800150 public ImsServiceProxy(Context context, int slotId, IBinder binder, int featureType) {
Brad Ebinger89fb5642017-11-06 15:17:32 -0800151 mSlotId = slotId;
152 mBinder = binder;
153 mSupportedFeature = featureType;
Brad Ebinger3418c132018-01-18 12:03:33 -0800154 mContext = context;
Brad Ebinger89fb5642017-11-06 15:17:32 -0800155 }
156
Brad Ebinger0e170c72017-12-14 14:24:02 -0800157 public ImsServiceProxy(Context context, int slotId, int featureType) {
Brad Ebinger3418c132018-01-18 12:03:33 -0800158 this(context, slotId, null, featureType);
Brad Ebinger0e170c72017-12-14 14:24:02 -0800159 }
160
161 public @Nullable IImsRegistration getRegistration() {
162 TelephonyManager tm = getTelephonyManager(mContext);
163 return tm != null ? tm.getImsRegistration(mSlotId, ImsFeature.MMTEL) : null;
Brad Ebinger89fb5642017-11-06 15:17:32 -0800164 }
165
Brad Ebingerf3cda822017-11-09 10:25:46 -0800166 public IImsServiceFeatureCallback getListener() {
Brad Ebinger89fb5642017-11-06 15:17:32 -0800167 return mListenerBinder;
168 }
169
170 public void setBinder(IBinder binder) {
171 mBinder = binder;
172 }
173
Brad Ebinger89fb5642017-11-06 15:17:32 -0800174 public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
175 throws RemoteException {
176 synchronized (mLock) {
177 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800178 return getServiceInterface(mBinder).startSession(incomingCallIntent, listener);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800179 }
180 }
181
Brad Ebinger89fb5642017-11-06 15:17:32 -0800182 public void endSession(int sessionId) throws RemoteException {
183 synchronized (mLock) {
184 // Only check to make sure the binder connection still exists. This method should
185 // still be able to be called when the state is STATE_NOT_AVAILABLE.
186 checkBinderConnection();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800187 getServiceInterface(mBinder).endSession(sessionId);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800188 }
189 }
190
Brad Ebinger89fb5642017-11-06 15:17:32 -0800191 public boolean isConnected(int callServiceType, int callType)
192 throws RemoteException {
193 synchronized (mLock) {
194 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800195 return getServiceInterface(mBinder).isConnected(callServiceType, callType);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800196 }
197 }
198
Brad Ebinger89fb5642017-11-06 15:17:32 -0800199 public boolean isOpened() throws RemoteException {
200 synchronized (mLock) {
201 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800202 return getServiceInterface(mBinder).isOpened();
Brad Ebinger89fb5642017-11-06 15:17:32 -0800203 }
204 }
205
Brad Ebinger89fb5642017-11-06 15:17:32 -0800206 public void addRegistrationListener(IImsRegistrationListener listener)
207 throws RemoteException {
208 synchronized (mLock) {
209 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800210 getServiceInterface(mBinder).addRegistrationListener(listener);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800211 }
212 }
213
Brad Ebinger89fb5642017-11-06 15:17:32 -0800214 public void removeRegistrationListener(IImsRegistrationListener listener)
215 throws RemoteException {
216 synchronized (mLock) {
217 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800218 getServiceInterface(mBinder).removeRegistrationListener(listener);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800219 }
220 }
221
Brad Ebinger89fb5642017-11-06 15:17:32 -0800222 public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
223 throws RemoteException {
224 synchronized (mLock) {
225 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800226 return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType,
227 callType);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800228 }
229 }
230
Brad Ebinger8409c972018-01-11 10:29:04 -0800231 public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
232 throws RemoteException {
Brad Ebinger89fb5642017-11-06 15:17:32 -0800233 synchronized (mLock) {
234 checkServiceIsReady();
Brad Ebinger8409c972018-01-11 10:29:04 -0800235 return getServiceInterface(mBinder).createCallSession(sessionId, profile);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800236 }
237 }
238
Brad Ebinger89fb5642017-11-06 15:17:32 -0800239 public IImsCallSession getPendingCallSession(int sessionId, String callId)
240 throws RemoteException {
241 synchronized (mLock) {
242 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800243 return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800244 }
245 }
246
Brad Ebinger89fb5642017-11-06 15:17:32 -0800247 public IImsUt getUtInterface() throws RemoteException {
248 synchronized (mLock) {
249 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800250 return getServiceInterface(mBinder).getUtInterface();
Brad Ebinger89fb5642017-11-06 15:17:32 -0800251 }
252 }
253
Brad Ebinger89fb5642017-11-06 15:17:32 -0800254 public IImsConfig getConfigInterface() throws RemoteException {
255 synchronized (mLock) {
256 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800257 return getServiceInterface(mBinder).getConfigInterface();
Brad Ebinger89fb5642017-11-06 15:17:32 -0800258 }
259 }
260
Brad Ebinger89fb5642017-11-06 15:17:32 -0800261 public void turnOnIms() throws RemoteException {
262 synchronized (mLock) {
263 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800264 getServiceInterface(mBinder).turnOnIms();
Brad Ebinger89fb5642017-11-06 15:17:32 -0800265 }
266 }
267
Brad Ebinger89fb5642017-11-06 15:17:32 -0800268 public void turnOffIms() throws RemoteException {
269 synchronized (mLock) {
270 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800271 getServiceInterface(mBinder).turnOffIms();
Brad Ebinger89fb5642017-11-06 15:17:32 -0800272 }
273 }
274
Brad Ebinger89fb5642017-11-06 15:17:32 -0800275 public IImsEcbm getEcbmInterface() throws RemoteException {
276 synchronized (mLock) {
277 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800278 return getServiceInterface(mBinder).getEcbmInterface();
Brad Ebinger89fb5642017-11-06 15:17:32 -0800279 }
280 }
281
Brad Ebinger89fb5642017-11-06 15:17:32 -0800282 public void setUiTTYMode(int uiTtyMode, Message onComplete)
283 throws RemoteException {
284 synchronized (mLock) {
285 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800286 getServiceInterface(mBinder).setUiTTYMode(uiTtyMode, onComplete);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800287 }
288 }
289
Brad Ebinger89fb5642017-11-06 15:17:32 -0800290 public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
291 synchronized (mLock) {
292 checkServiceIsReady();
Brad Ebingerf3cda822017-11-09 10:25:46 -0800293 return getServiceInterface(mBinder).getMultiEndpointInterface();
Brad Ebinger89fb5642017-11-06 15:17:32 -0800294 }
295 }
296
297 /**
298 * @return an integer describing the current Feature Status, defined in
299 * {@link ImsFeature.ImsState}.
300 */
301 public int getFeatureStatus() {
302 synchronized (mLock) {
303 if (isBinderAlive() && mFeatureStatusCached != null) {
Brad Ebinger0e170c72017-12-14 14:24:02 -0800304 Log.i(TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800305 return mFeatureStatusCached;
306 }
307 }
308 // Don't synchronize on Binder call.
309 Integer status = retrieveFeatureStatus();
310 synchronized (mLock) {
311 if (status == null) {
312 return ImsFeature.STATE_NOT_AVAILABLE;
313 }
314 // Cache only non-null value for feature status.
315 mFeatureStatusCached = status;
316 }
Brad Ebinger0e170c72017-12-14 14:24:02 -0800317 Log.i(TAG, "getFeatureStatus - returning " + status);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800318 return status;
319 }
320
321 /**
322 * Internal method used to retrieve the feature status from the corresponding ImsService.
323 */
324 private Integer retrieveFeatureStatus() {
325 if (mBinder != null) {
326 try {
Brad Ebingerf3cda822017-11-09 10:25:46 -0800327 return getServiceInterface(mBinder).getFeatureStatus();
Brad Ebinger89fb5642017-11-06 15:17:32 -0800328 } catch (RemoteException e) {
329 // Status check failed, don't update cache
330 }
331 }
332 return null;
333 }
334
335 /**
336 * @param c Callback that will fire when the feature status has changed.
337 */
Brad Ebinger0e170c72017-12-14 14:24:02 -0800338 public void setStatusCallback(IFeatureUpdate c) {
Brad Ebinger89fb5642017-11-06 15:17:32 -0800339 mStatusCallback = c;
340 }
341
Mohamed Abdalkaderf8800da2018-01-10 13:29:26 -0800342 public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
343 byte[] pdu) throws RemoteException {
344 synchronized (mLock) {
345 checkServiceIsReady();
346 getServiceInterface(mBinder).sendSms(token, messageRef, format, smsc, isRetry,
347 pdu);
348 }
349 }
350
351 public void acknowledgeSms(int token, int messageRef,
352 @SmsImplBase.SendStatusResult int result) throws RemoteException {
353 synchronized (mLock) {
354 checkServiceIsReady();
355 getServiceInterface(mBinder).acknowledgeSms(token, messageRef, result);
356 }
357 }
358
359 public void acknowledgeSmsReport(int token, int messageRef,
360 @SmsImplBase.StatusReportResult int result) throws RemoteException {
361 synchronized (mLock) {
362 checkServiceIsReady();
363 getServiceInterface(mBinder).acknowledgeSmsReport(token, messageRef, result);
364 }
365 }
366
367 public String getSmsFormat() throws RemoteException {
368 synchronized (mLock) {
369 checkServiceIsReady();
370 return getServiceInterface(mBinder).getSmsFormat();
371 }
372 }
373
Mohamed Abdalkader4d59b1b2018-01-23 13:11:00 -0800374 public void onSmsReady() throws RemoteException {
375 synchronized (mLock) {
376 checkServiceIsReady();
377 getServiceInterface(mBinder).onSmsReady();
378 }
379 }
380
Mohamed Abdalkaderf8800da2018-01-10 13:29:26 -0800381 public void setSmsListener(IImsSmsListener listener) throws RemoteException {
382 synchronized (mLock) {
383 checkServiceIsReady();
384 getServiceInterface(mBinder).setSmsListener(listener);
385 }
386 }
387
Brad Ebinger89fb5642017-11-06 15:17:32 -0800388 /**
389 * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
390 * method returns false, it doesn't mean that the Binder connection is not available (use
391 * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
392 * at this time.
393 *
394 * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
395 * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}.
396 */
397 public boolean isBinderReady() {
398 return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY;
399 }
400
401 /**
402 * @return false if the binder connection is no longer alive.
403 */
404 public boolean isBinderAlive() {
405 return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
406 }
407
408 protected void checkServiceIsReady() throws RemoteException {
409 if (!isBinderReady()) {
410 throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
411 }
412 }
413
Brad Ebingerf3cda822017-11-09 10:25:46 -0800414 private IImsMMTelFeature getServiceInterface(IBinder b) {
415 return IImsMMTelFeature.Stub.asInterface(b);
Brad Ebinger89fb5642017-11-06 15:17:32 -0800416 }
417
418 protected void checkBinderConnection() throws RemoteException {
419 if (!isBinderAlive()) {
420 throw new RemoteException("ImsServiceProxy is not available for that feature.");
421 }
422 }
423}