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.cts.embmstestapp; 18 19 import android.app.Service; 20 import android.content.ComponentName; 21 import android.content.Intent; 22 import android.net.Uri; 23 import android.os.Binder; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.IBinder; 27 import android.os.RemoteException; 28 import android.telephony.mbms.GroupCall; 29 import android.telephony.mbms.MbmsErrors; 30 import android.telephony.mbms.MbmsGroupCallSessionCallback; 31 import android.telephony.mbms.GroupCallCallback; 32 import android.telephony.mbms.vendor.MbmsGroupCallServiceBase; 33 import android.util.Log; 34 35 import java.util.Arrays; 36 import java.util.Collections; 37 import java.util.HashSet; 38 import java.util.LinkedList; 39 import java.util.List; 40 import java.util.Set; 41 42 public class CtsGroupCallService extends MbmsGroupCallServiceBase { 43 private static final Set<String> ALLOWED_PACKAGES = new HashSet<String>() {{ 44 add("android.telephony.cts"); 45 }}; 46 private static final String TAG = "EmbmsTestGroupCall"; 47 48 public static final String METHOD_INITIALIZE = "initialize"; 49 public static final String METHOD_START_GROUP_CALL = "startGroupCall"; 50 public static final String METHOD_UPDATE_GROUP_CALL = "updateGroupCall"; 51 public static final String METHOD_STOP_GROUP_CALL = "stopGroupCall"; 52 public static final String METHOD_CLOSE = "close"; 53 54 public static final String CONTROL_INTERFACE_ACTION = 55 "android.telephony.cts.embmstestapp.ACTION_CONTROL_MIDDLEWARE"; 56 public static final ComponentName CONTROL_INTERFACE_COMPONENT = 57 ComponentName.unflattenFromString( 58 "android.telephony.cts.embmstestapp/.CtsGroupCallService"); 59 60 private MbmsGroupCallSessionCallback mAppCallback; 61 private GroupCallCallback mGroupCallCallback; 62 63 private HandlerThread mHandlerThread; 64 private Handler mHandler; 65 private List<List> mReceivedCalls = new LinkedList<>(); 66 private int mErrorCodeOverride = MbmsErrors.SUCCESS; 67 68 @Override 69 public int initialize(MbmsGroupCallSessionCallback callback, int subId) { 70 mReceivedCalls.add(Arrays.asList(METHOD_INITIALIZE, subId)); 71 if (mErrorCodeOverride != MbmsErrors.SUCCESS) { 72 return mErrorCodeOverride; 73 } 74 75 int packageUid = Binder.getCallingUid(); 76 String[] packageNames = getPackageManager().getPackagesForUid(packageUid); 77 if (packageNames == null) { 78 return MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED; 79 } 80 boolean isUidAllowed = Arrays.stream(packageNames).anyMatch(ALLOWED_PACKAGES::contains); 81 if (!isUidAllowed) { 82 return MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED; 83 } 84 85 mHandler.post(() -> { 86 if (mAppCallback == null) { 87 mAppCallback = callback; 88 } else { 89 callback.onError( 90 MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE, ""); 91 return; 92 } 93 callback.onMiddlewareReady(); 94 }); 95 return MbmsErrors.SUCCESS; 96 } 97 98 @Override 99 public int startGroupCall(final int subscriptionId, final long tmgi, 100 final List<Integer> saiArray, final List<Integer> frequencyArray, 101 final GroupCallCallback callback) { 102 mReceivedCalls.add(Arrays.asList(METHOD_START_GROUP_CALL, subscriptionId, tmgi, saiArray, 103 frequencyArray)); 104 if (mErrorCodeOverride != MbmsErrors.SUCCESS) { 105 return mErrorCodeOverride; 106 } 107 108 mGroupCallCallback = callback; 109 mHandler.post(() -> callback.onGroupCallStateChanged(GroupCall.STATE_STARTED, 110 GroupCall.REASON_BY_USER_REQUEST)); 111 return MbmsErrors.SUCCESS; 112 } 113 114 @Override 115 public void updateGroupCall(int subscriptionId, long tmgi, 116 List<Integer> saiArray, List<Integer> frequencyArray) { 117 mReceivedCalls.add(Arrays.asList(METHOD_UPDATE_GROUP_CALL, 118 subscriptionId, tmgi, saiArray, frequencyArray)); 119 } 120 121 @Override 122 public void stopGroupCall(int subscriptionId, long tmgi) { 123 mReceivedCalls.add(Arrays.asList(METHOD_STOP_GROUP_CALL, subscriptionId, tmgi)); 124 } 125 126 @Override 127 public void dispose(int subscriptionId) { 128 mReceivedCalls.add(Arrays.asList(METHOD_CLOSE, subscriptionId)); 129 } 130 131 @Override 132 public void onAppCallbackDied(int uid, int subscriptionId) { 133 mAppCallback = null; 134 } 135 136 private final IBinder mControlInterface = new ICtsGroupCallMiddlewareControl.Stub() { 137 @Override 138 public void reset() { 139 mReceivedCalls.clear(); 140 mHandler.removeCallbacksAndMessages(null); 141 mAppCallback = null; 142 mErrorCodeOverride = MbmsErrors.SUCCESS; 143 } 144 145 @Override 146 public List getGroupCallSessionCalls() { 147 return mReceivedCalls; 148 } 149 150 @Override 151 public void forceErrorCode(int error) { 152 mErrorCodeOverride = error; 153 } 154 155 @Override 156 public void fireErrorOnGroupCall(int errorCode, String message) { 157 mHandler.post(() -> mGroupCallCallback.onError(errorCode, message)); 158 } 159 160 @Override 161 public void fireErrorOnSession(int errorCode, String message) { 162 mHandler.post(() -> mAppCallback.onError(errorCode, message)); 163 } 164 165 @Override 166 public void fireGroupCallStateChanged(int state, int reason) { 167 mHandler.post(() -> mGroupCallCallback.onGroupCallStateChanged(state, reason)); 168 } 169 170 @Override 171 public void fireBroadcastSignalStrengthUpdated(int signalStrength) { 172 mHandler.post( 173 () -> mGroupCallCallback.onBroadcastSignalStrengthUpdated(signalStrength)); 174 } 175 176 @Override 177 public void fireAvailableSaisUpdated(List currentSais, List availableSais) { 178 mHandler.post(() -> mAppCallback.onAvailableSaisUpdated(currentSais, availableSais)); 179 } 180 181 @Override 182 public void fireServiceInterfaceAvailable(String interfaceName, int index) { 183 mHandler.post(() -> mAppCallback.onServiceInterfaceAvailable(interfaceName, index)); 184 } 185 }; 186 187 @Override 188 public void onDestroy() { 189 mHandlerThread.quitSafely(); 190 logd("CtsGroupCallService onDestroy"); 191 super.onDestroy(); 192 } 193 194 @Override 195 public IBinder onBind(Intent intent) { 196 logd("CtsGroupCallService onBind"); 197 if (CONTROL_INTERFACE_ACTION.equals(intent.getAction())) { 198 logd("CtsGroupCallService control interface bind"); 199 return mControlInterface; 200 } 201 IBinder binder = super.onBind(intent); 202 203 if (mHandlerThread != null && mHandlerThread.isAlive()) { 204 return binder; 205 } 206 207 mHandlerThread = new HandlerThread("CtsGroupCallServiceWorker"); 208 mHandlerThread.start(); 209 mHandler = new Handler(mHandlerThread.getLooper()); 210 return binder; 211 } 212 213 private static void logd(String s) { 214 Log.d(TAG, s); 215 } 216 217 private void checkInitialized() { 218 if (mAppCallback == null) { 219 throw new IllegalStateException("Not yet initialized"); 220 } 221 } 222 } 223