1 /* 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 17 package android.telephony.ims.stub; 18 19 import android.annotation.NonNull; 20 import android.annotation.SystemApi; 21 import android.os.RemoteException; 22 import android.util.Log; 23 24 import com.android.ims.internal.IImsEcbm; 25 import com.android.ims.internal.IImsEcbmListener; 26 import com.android.internal.telephony.util.TelephonyUtils; 27 28 import java.util.Objects; 29 import java.util.concurrent.CancellationException; 30 import java.util.concurrent.CompletableFuture; 31 import java.util.concurrent.CompletionException; 32 import java.util.concurrent.Executor; 33 34 35 /** 36 * Base implementation of ImsEcbm, which implements stub versions of the methods 37 * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports. 38 * 39 * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you 40 * will break other implementations of ImsEcbm maintained by other ImsServices. 41 * 42 * @hide 43 */ 44 @SystemApi 45 public class ImsEcbmImplBase { 46 private static final String TAG = "ImsEcbmImplBase"; 47 48 private final Object mLock = new Object(); 49 private IImsEcbmListener mListener; 50 private Executor mExecutor = Runnable::run; 51 52 private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() { 53 @Override 54 public void setListener(IImsEcbmListener listener) { 55 executeMethodAsync(() -> { 56 if (mListener != null && !mListener.asBinder().isBinderAlive()) { 57 Log.w(TAG, "setListener: discarding dead Binder"); 58 mListener = null; 59 } 60 if (mListener != null && listener != null && Objects.equals( 61 mListener.asBinder(), listener.asBinder())) { 62 return; 63 } 64 if (listener == null) { 65 mListener = null; 66 } else if (listener != null && mListener == null) { 67 mListener = listener; 68 } else { 69 // Warn that the listener is being replaced while active 70 Log.w(TAG, "setListener is being called when there is already an active " 71 + "listener"); 72 mListener = listener; 73 } 74 }, "setListener"); 75 } 76 77 @Override 78 public void exitEmergencyCallbackMode() { 79 executeMethodAsync(() -> ImsEcbmImplBase.this.exitEmergencyCallbackMode(), 80 "exitEmergencyCallbackMode"); 81 } 82 83 // Call the methods with a clean calling identity on the executor and wait indefinitely for 84 // the future to return. 85 private void executeMethodAsync(Runnable r, String errorLogName) { 86 try { 87 CompletableFuture.runAsync( 88 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); 89 } catch (CancellationException | CompletionException e) { 90 Log.w(TAG, "ImsEcbmImplBase Binder - " + errorLogName + " exception: " 91 + e.getMessage()); 92 } 93 } 94 }; 95 96 /** @hide */ getImsEcbm()97 public IImsEcbm getImsEcbm() { 98 return mImsEcbm; 99 } 100 101 /** 102 * This method should be implemented by the IMS provider. Framework will trigger this method to 103 * request to come out of ECBM mode 104 */ exitEmergencyCallbackMode()105 public void exitEmergencyCallbackMode() { 106 Log.d(TAG, "exitEmergencyCallbackMode() not implemented"); 107 } 108 109 /** 110 * Notifies the framework when the device enters Emergency Callback Mode. 111 * 112 * @throws RuntimeException if the connection to the framework is not available. 113 */ enteredEcbm()114 public final void enteredEcbm() { 115 Log.d(TAG, "Entered ECBM."); 116 IImsEcbmListener listener; 117 synchronized (mLock) { 118 listener = mListener; 119 } 120 if (listener != null) { 121 try { 122 listener.enteredECBM(); 123 } catch (RemoteException e) { 124 throw new RuntimeException(e); 125 } 126 } 127 } 128 129 /** 130 * Notifies the framework when the device exits Emergency Callback Mode. 131 * 132 * @throws RuntimeException if the connection to the framework is not available. 133 */ exitedEcbm()134 public final void exitedEcbm() { 135 Log.d(TAG, "Exited ECBM."); 136 IImsEcbmListener listener; 137 synchronized (mLock) { 138 listener = mListener; 139 } 140 if (listener != null) { 141 try { 142 listener.exitedECBM(); 143 } catch (RemoteException e) { 144 throw new RuntimeException(e); 145 } 146 } 147 } 148 149 /** 150 * Set default Executor from MmTelFeature. 151 * @param executor The default executor for the framework to use when executing the methods 152 * overridden by the implementation of ImsEcbm. 153 * @hide 154 */ setDefaultExecutor(@onNull Executor executor)155 public final void setDefaultExecutor(@NonNull Executor executor) { 156 mExecutor = executor; 157 } 158 } 159