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.internal.telephony.domainselection; 18 19 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING; 20 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.os.AsyncResult; 26 import android.os.Handler; 27 import android.os.HandlerThread; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.telephony.BarringInfo; 31 import android.telephony.DomainSelectionService; 32 import android.telephony.ServiceState; 33 import android.telephony.TelephonyManager; 34 import android.telephony.TransportSelectorCallback; 35 import android.util.LocalLog; 36 import android.util.Log; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.internal.telephony.Phone; 40 import com.android.internal.telephony.util.TelephonyUtils; 41 42 import java.io.PrintWriter; 43 import java.util.ArrayList; 44 import java.util.concurrent.Executor; 45 46 /** 47 * Manages the connection to {@link DomainSelectionService}. 48 */ 49 public class DomainSelectionController { 50 private static final String TAG = "DomainSelectionController"; 51 private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE; 52 53 private static final int EVENT_SERVICE_STATE_CHANGED = 1; 54 private static final int EVENT_BARRING_INFO_CHANGED = 2; 55 56 private final HandlerThread mHandlerThread = 57 new HandlerThread("DomainSelectionControllerHandler"); 58 59 private final DomainSelectionService mDomainSelectionService; 60 private final Handler mHandler; 61 // Only added or removed, never accessed on purpose. 62 private final LocalLog mLocalLog = new LocalLog(30); 63 64 protected final Object mLock = new Object(); 65 protected final Context mContext; 66 67 protected final int[] mConnectionCounts; 68 private final ArrayList<DomainSelectionConnection> mConnections = new ArrayList<>(); 69 70 private final class DomainSelectionControllerHandler extends Handler { DomainSelectionControllerHandler(Looper looper)71 DomainSelectionControllerHandler(Looper looper) { 72 super(looper); 73 } 74 75 @Override handleMessage(Message msg)76 public void handleMessage(Message msg) { 77 AsyncResult ar; 78 switch (msg.what) { 79 case EVENT_SERVICE_STATE_CHANGED: 80 ar = (AsyncResult) msg.obj; 81 updateServiceState((Phone) ar.userObj, (ServiceState) ar.result); 82 break; 83 case EVENT_BARRING_INFO_CHANGED: 84 ar = (AsyncResult) msg.obj; 85 updateBarringInfo((Phone) ar.userObj, (BarringInfo) ar.result); 86 break; 87 default: 88 loge("unexpected event=" + msg.what); 89 break; 90 } 91 } 92 } 93 94 /** 95 * Creates an instance. 96 * 97 * @param context Context object from hosting application. 98 * @param service The {@link DomainSelectionService} instance. 99 */ DomainSelectionController(@onNull Context context, @NonNull DomainSelectionService service)100 public DomainSelectionController(@NonNull Context context, 101 @NonNull DomainSelectionService service) { 102 this(context, service, null); 103 } 104 105 /** 106 * Creates an instance. 107 * 108 * @param context Context object from hosting application. 109 * @param service The {@link DomainSelectionService} instance. 110 * @param looper Handles event messages. 111 */ 112 @VisibleForTesting DomainSelectionController(@onNull Context context, @NonNull DomainSelectionService service, @Nullable Looper looper)113 public DomainSelectionController(@NonNull Context context, 114 @NonNull DomainSelectionService service, @Nullable Looper looper) { 115 mContext = context; 116 mDomainSelectionService = service; 117 118 if (looper == null) { 119 mHandlerThread.start(); 120 looper = mHandlerThread.getLooper(); 121 } 122 mHandler = new DomainSelectionControllerHandler(looper); 123 124 int numPhones = TelephonyManager.getDefault().getActiveModemCount(); 125 mConnectionCounts = new int[numPhones]; 126 for (int i = 0; i < numPhones; i++) { 127 mConnectionCounts[i] = 0; 128 } 129 } 130 131 /** 132 * Returns a {@link DomainSelectionConnection} instance. 133 * 134 * @param phone Indicates who requests the service. 135 * @param selectorType Indicates the selector type requested. 136 * @param isEmergency Indicates whether this is for emergency service. 137 * @return A {@link DomainSelectiionConnection} instance for the requested service. 138 * Returns {@code null} if the requested service is not supported. 139 */ getDomainSelectionConnection( @onNull Phone phone, @DomainSelectionService.SelectorType int selectorType, boolean isEmergency)140 public @Nullable DomainSelectionConnection getDomainSelectionConnection( 141 @NonNull Phone phone, 142 @DomainSelectionService.SelectorType int selectorType, 143 boolean isEmergency) { 144 DomainSelectionConnection c = null; 145 146 if (selectorType == SELECTOR_TYPE_CALLING) { 147 if (isEmergency) { 148 c = new EmergencyCallDomainSelectionConnection(phone, this); 149 } else { 150 c = new NormalCallDomainSelectionConnection(phone, this); 151 } 152 } else if (selectorType == SELECTOR_TYPE_SMS) { 153 if (isEmergency) { 154 c = new EmergencySmsDomainSelectionConnection(phone, this); 155 } else { 156 c = new SmsDomainSelectionConnection(phone, this); 157 } 158 } 159 160 addConnection(c); 161 return c; 162 } 163 addConnection(@ullable DomainSelectionConnection c)164 private void addConnection(@Nullable DomainSelectionConnection c) { 165 if (c == null) return; 166 mConnections.add(c); 167 registerForStateChange(c); 168 } 169 170 /** 171 * Releases resources for this connection. 172 */ removeConnection(@ullable DomainSelectionConnection c)173 public void removeConnection(@Nullable DomainSelectionConnection c) { 174 if (c == null) return; 175 mConnections.remove(c); 176 unregisterForStateChange(c); 177 } 178 179 /** 180 * Requests the domain selection. 181 * 182 * @param attr Attributetes required to determine the domain. 183 * @param callback A callback to receive the response. 184 */ selectDomain(@onNull DomainSelectionService.SelectionAttributes attr, @NonNull TransportSelectorCallback callback)185 public void selectDomain(@NonNull DomainSelectionService.SelectionAttributes attr, 186 @NonNull TransportSelectorCallback callback) { 187 if (attr == null || callback == null) return; 188 if (DBG) logd("selectDomain"); 189 190 Executor e = mDomainSelectionService.getCachedExecutor(); 191 e.execute(() -> mDomainSelectionService.onDomainSelection(attr, callback)); 192 } 193 194 /** 195 * Notifies the change in {@link ServiceState} for a specific slot. 196 * 197 * @param phone {@link Phone} which the state changed. 198 * @param serviceState Updated {@link ServiceState}. 199 */ updateServiceState(Phone phone, ServiceState serviceState)200 private void updateServiceState(Phone phone, ServiceState serviceState) { 201 if (phone == null || serviceState == null) return; 202 if (DBG) logd("updateServiceState phoneId=" + phone.getPhoneId()); 203 204 Executor e = mDomainSelectionService.getCachedExecutor(); 205 e.execute(() -> mDomainSelectionService.onServiceStateUpdated( 206 phone.getPhoneId(), phone.getSubId(), serviceState)); 207 } 208 209 /** 210 * Notifies the change in {@link BarringInfo} for a specific slot. 211 * 212 * @param phone {@link Phone} which the state changed. 213 * @param info Updated {@link BarringInfo}. 214 */ updateBarringInfo(Phone phone, BarringInfo info)215 private void updateBarringInfo(Phone phone, BarringInfo info) { 216 if (phone == null || info == null) return; 217 if (DBG) logd("updateBarringInfo phoneId=" + phone.getPhoneId()); 218 219 Executor e = mDomainSelectionService.getCachedExecutor(); 220 e.execute(() -> mDomainSelectionService.onBarringInfoUpdated( 221 phone.getPhoneId(), phone.getSubId(), info)); 222 } 223 224 /** 225 * Registers for the notification of {@link ServiceState} and {@link BarringInfo}. 226 * 227 * @param c {@link DomainSelectionConnection} for which the registration is requested. 228 */ registerForStateChange(DomainSelectionConnection c)229 private void registerForStateChange(DomainSelectionConnection c) { 230 Phone phone = c.getPhone(); 231 int count = mConnectionCounts[phone.getPhoneId()]; 232 if (count < 0) count = 0; 233 234 mConnectionCounts[phone.getPhoneId()] = count + 1; 235 if (count > 0) return; 236 237 phone.registerForServiceStateChanged(mHandler, EVENT_SERVICE_STATE_CHANGED, phone); 238 phone.mCi.registerForBarringInfoChanged(mHandler, EVENT_BARRING_INFO_CHANGED, phone); 239 240 updateServiceState(phone, phone.getServiceStateTracker().getServiceState()); 241 updateBarringInfo(phone, phone.mCi.getLastBarringInfo()); 242 } 243 244 /** 245 * Unregisters for the notification of {@link ServiceState} and {@link BarringInfo}. 246 * 247 * @param c {@link DomainSelectionConnection} for which the unregistration is requested. 248 */ unregisterForStateChange(DomainSelectionConnection c)249 private void unregisterForStateChange(DomainSelectionConnection c) { 250 Phone phone = c.getPhone(); 251 int count = mConnectionCounts[phone.getPhoneId()]; 252 if (count < 1) count = 1; 253 254 mConnectionCounts[phone.getPhoneId()] = count - 1; 255 if (count > 1) return; 256 257 phone.unregisterForServiceStateChanged(mHandler); 258 phone.mCi.unregisterForBarringInfoChanged(mHandler); 259 } 260 261 /** 262 * Notifies the {@link DomainSelectionConnection} instances registered 263 * of the service disconnection. 264 */ notifyServiceDisconnected()265 private void notifyServiceDisconnected() { 266 for (DomainSelectionConnection c : mConnections) { 267 c.onServiceDisconnected(); 268 } 269 } 270 271 /** 272 * Gets the {@link Executor} which executes methods of {@link DomainSelectionService.} 273 * @return {@link Executor} instance. 274 */ getDomainSelectionServiceExecutor()275 public @NonNull Executor getDomainSelectionServiceExecutor() { 276 return mDomainSelectionService.getCachedExecutor(); 277 } 278 279 /** 280 * Dumps logcal log 281 */ dump(@onNull PrintWriter printWriter)282 public void dump(@NonNull PrintWriter printWriter) { 283 mLocalLog.dump(printWriter); 284 } 285 logd(String msg)286 private void logd(String msg) { 287 Log.d(TAG, msg); 288 } 289 logi(String msg)290 private void logi(String msg) { 291 Log.i(TAG, msg); 292 mLocalLog.log(msg); 293 } 294 loge(String msg)295 private void loge(String msg) { 296 Log.e(TAG, msg); 297 mLocalLog.log(msg); 298 } 299 } 300