1 /* 2 * Copyright (C) 2015 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; 18 19 import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX; 20 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; 21 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.net.MatchAllNetworkSpecifier; 27 import android.net.NetworkCapabilities; 28 import android.net.NetworkFactory; 29 import android.net.NetworkRequest; 30 import android.net.NetworkSpecifier; 31 import android.net.StringNetworkSpecifier; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.Registrant; 36 import android.os.RegistrantList; 37 import android.os.RemoteException; 38 import android.telephony.Rlog; 39 import android.util.LocalLog; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.telephony.dataconnection.DcRequest; 43 import com.android.internal.util.IndentingPrintWriter; 44 45 import java.io.FileDescriptor; 46 import java.io.PrintWriter; 47 import java.util.ArrayList; 48 import java.util.Calendar; 49 import java.util.Collections; 50 import java.util.List; 51 52 /** 53 * Utility singleton to monitor subscription changes and incoming NetworkRequests 54 * and determine which phone/phones are active. 55 * 56 * Manages the ALLOW_DATA calls to modems and notifies phones about changes to 57 * the active phones. Note we don't wait for data attach (which may not happen anyway). 58 */ 59 public class PhoneSwitcher extends Handler { 60 private final static String LOG_TAG = "PhoneSwitcher"; 61 private final static boolean VDBG = false; 62 63 private final int mMaxActivePhones; 64 private final List<DcRequest> mPrioritizedDcRequests = new ArrayList<DcRequest>(); 65 private final RegistrantList[] mActivePhoneRegistrants; 66 private final SubscriptionController mSubscriptionController; 67 private final int[] mPhoneSubscriptions; 68 private final CommandsInterface[] mCommandsInterfaces; 69 private final Context mContext; 70 private final PhoneState[] mPhoneStates; 71 private final int mNumPhones; 72 private final Phone[] mPhones; 73 private final LocalLog mLocalLog; 74 75 private int mDefaultDataSubscription; 76 77 private final static int EVENT_DEFAULT_SUBSCRIPTION_CHANGED = 101; 78 private final static int EVENT_SUBSCRIPTION_CHANGED = 102; 79 private final static int EVENT_REQUEST_NETWORK = 103; 80 private final static int EVENT_RELEASE_NETWORK = 104; 81 private final static int EVENT_EMERGENCY_TOGGLE = 105; 82 private final static int EVENT_RESEND_DATA_ALLOWED = 106; 83 84 private final static int MAX_LOCAL_LOG_LINES = 30; 85 86 @VisibleForTesting 87 public PhoneSwitcher(Looper looper) { 88 super(looper); 89 mMaxActivePhones = 0; 90 mSubscriptionController = null; 91 mPhoneSubscriptions = null; 92 mCommandsInterfaces = null; 93 mContext = null; 94 mPhoneStates = null; 95 mPhones = null; 96 mLocalLog = null; 97 mActivePhoneRegistrants = null; 98 mNumPhones = 0; 99 } 100 101 public PhoneSwitcher(int maxActivePhones, int numPhones, Context context, 102 SubscriptionController subscriptionController, Looper looper, ITelephonyRegistry tr, 103 CommandsInterface[] cis, Phone[] phones) { 104 super(looper); 105 mContext = context; 106 mNumPhones = numPhones; 107 mPhones = phones; 108 mPhoneSubscriptions = new int[numPhones]; 109 mMaxActivePhones = maxActivePhones; 110 mLocalLog = new LocalLog(MAX_LOCAL_LOG_LINES); 111 112 mSubscriptionController = subscriptionController; 113 114 mActivePhoneRegistrants = new RegistrantList[numPhones]; 115 mPhoneStates = new PhoneState[numPhones]; 116 for (int i = 0; i < numPhones; i++) { 117 mActivePhoneRegistrants[i] = new RegistrantList(); 118 mPhoneStates[i] = new PhoneState(); 119 if (mPhones[i] != null) { 120 mPhones[i].registerForEmergencyCallToggle(this, EVENT_EMERGENCY_TOGGLE, null); 121 } 122 } 123 124 mCommandsInterfaces = cis; 125 126 try { 127 tr.addOnSubscriptionsChangedListener(context.getOpPackageName(), 128 mSubscriptionsChangedListener); 129 } catch (RemoteException e) { 130 } 131 132 mContext.registerReceiver(mDefaultDataChangedReceiver, 133 new IntentFilter(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)); 134 135 NetworkCapabilities netCap = new NetworkCapabilities(); 136 netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); 137 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); 138 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL); 139 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); 140 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA); 141 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); 142 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS); 143 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IA); 144 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS); 145 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP); 146 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS); 147 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 148 netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 149 netCap.setNetworkSpecifier(new MatchAllNetworkSpecifier()); 150 151 NetworkFactory networkFactory = new PhoneSwitcherNetworkRequestListener(looper, context, 152 netCap, this); 153 // we want to see all requests 154 networkFactory.setScoreFilter(101); 155 networkFactory.register(); 156 157 log("PhoneSwitcher started"); 158 } 159 160 private final BroadcastReceiver mDefaultDataChangedReceiver = new BroadcastReceiver() { 161 @Override 162 public void onReceive(Context context, Intent intent) { 163 Message msg = PhoneSwitcher.this.obtainMessage(EVENT_DEFAULT_SUBSCRIPTION_CHANGED); 164 msg.sendToTarget(); 165 } 166 }; 167 168 private final IOnSubscriptionsChangedListener mSubscriptionsChangedListener = 169 new IOnSubscriptionsChangedListener.Stub() { 170 @Override 171 public void onSubscriptionsChanged() { 172 Message msg = PhoneSwitcher.this.obtainMessage(EVENT_SUBSCRIPTION_CHANGED); 173 msg.sendToTarget(); 174 } 175 }; 176 177 @Override 178 public void handleMessage(Message msg) { 179 switch (msg.what) { 180 case EVENT_SUBSCRIPTION_CHANGED: { 181 onEvaluate(REQUESTS_UNCHANGED, "subChanged"); 182 break; 183 } 184 case EVENT_DEFAULT_SUBSCRIPTION_CHANGED: { 185 onEvaluate(REQUESTS_UNCHANGED, "defaultChanged"); 186 break; 187 } 188 case EVENT_REQUEST_NETWORK: { 189 onRequestNetwork((NetworkRequest)msg.obj); 190 break; 191 } 192 case EVENT_RELEASE_NETWORK: { 193 onReleaseNetwork((NetworkRequest)msg.obj); 194 break; 195 } 196 case EVENT_EMERGENCY_TOGGLE: { 197 onEvaluate(REQUESTS_CHANGED, "emergencyToggle"); 198 break; 199 } 200 case EVENT_RESEND_DATA_ALLOWED: { 201 onResendDataAllowed(msg); 202 break; 203 } 204 } 205 } 206 207 private boolean isEmergency() { 208 for (Phone p : mPhones) { 209 if (p == null) continue; 210 if (p.isInEcm() || p.isInEmergencyCall()) return true; 211 } 212 return false; 213 } 214 215 private static class PhoneSwitcherNetworkRequestListener extends NetworkFactory { 216 private final PhoneSwitcher mPhoneSwitcher; 217 public PhoneSwitcherNetworkRequestListener (Looper l, Context c, 218 NetworkCapabilities nc, PhoneSwitcher ps) { 219 super(l, c, "PhoneSwitcherNetworkRequstListener", nc); 220 mPhoneSwitcher = ps; 221 } 222 223 @Override 224 protected void needNetworkFor(NetworkRequest networkRequest, int score) { 225 if (VDBG) log("needNetworkFor " + networkRequest + ", " + score); 226 Message msg = mPhoneSwitcher.obtainMessage(EVENT_REQUEST_NETWORK); 227 msg.obj = networkRequest; 228 msg.sendToTarget(); 229 } 230 231 @Override 232 protected void releaseNetworkFor(NetworkRequest networkRequest) { 233 if (VDBG) log("releaseNetworkFor " + networkRequest); 234 Message msg = mPhoneSwitcher.obtainMessage(EVENT_RELEASE_NETWORK); 235 msg.obj = networkRequest; 236 msg.sendToTarget(); 237 } 238 } 239 240 private void onRequestNetwork(NetworkRequest networkRequest) { 241 final DcRequest dcRequest = new DcRequest(networkRequest, mContext); 242 if (mPrioritizedDcRequests.contains(dcRequest) == false) { 243 mPrioritizedDcRequests.add(dcRequest); 244 Collections.sort(mPrioritizedDcRequests); 245 onEvaluate(REQUESTS_CHANGED, "netRequest"); 246 } 247 } 248 249 private void onReleaseNetwork(NetworkRequest networkRequest) { 250 final DcRequest dcRequest = new DcRequest(networkRequest, mContext); 251 252 if (mPrioritizedDcRequests.remove(dcRequest)) { 253 onEvaluate(REQUESTS_CHANGED, "netReleased"); 254 } 255 } 256 257 private static final boolean REQUESTS_CHANGED = true; 258 private static final boolean REQUESTS_UNCHANGED = false; 259 /** 260 * Re-evaluate things. 261 * Do nothing if nothing's changed. 262 * 263 * Otherwise, go through the requests in priority order adding their phone 264 * until we've added up to the max allowed. Then go through shutting down 265 * phones that aren't in the active phone list. Finally, activate all 266 * phones in the active phone list. 267 */ 268 private void onEvaluate(boolean requestsChanged, String reason) { 269 StringBuilder sb = new StringBuilder(reason); 270 if (isEmergency()) { 271 log("onEvalute aborted due to Emergency"); 272 return; 273 } 274 275 boolean diffDetected = requestsChanged; 276 final int dataSub = mSubscriptionController.getDefaultDataSubId(); 277 if (dataSub != mDefaultDataSubscription) { 278 sb.append(" default ").append(mDefaultDataSubscription).append("->").append(dataSub); 279 mDefaultDataSubscription = dataSub; 280 diffDetected = true; 281 282 } 283 284 for (int i = 0; i < mNumPhones; i++) { 285 int sub = mSubscriptionController.getSubIdUsingPhoneId(i); 286 if (sub != mPhoneSubscriptions[i]) { 287 sb.append(" phone[").append(i).append("] ").append(mPhoneSubscriptions[i]); 288 sb.append("->").append(sub); 289 mPhoneSubscriptions[i] = sub; 290 diffDetected = true; 291 } 292 } 293 294 if (diffDetected) { 295 log("evaluating due to " + sb.toString()); 296 297 List<Integer> newActivePhones = new ArrayList<Integer>(); 298 299 for (DcRequest dcRequest : mPrioritizedDcRequests) { 300 int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest); 301 if (phoneIdForRequest == INVALID_PHONE_INDEX) continue; 302 if (newActivePhones.contains(phoneIdForRequest)) continue; 303 newActivePhones.add(phoneIdForRequest); 304 if (newActivePhones.size() >= mMaxActivePhones) break; 305 } 306 307 if (VDBG) { 308 log("default subId = " + mDefaultDataSubscription); 309 for (int i = 0; i < mNumPhones; i++) { 310 log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]"); 311 } 312 log(" newActivePhones:"); 313 for (Integer i : newActivePhones) log(" " + i); 314 } 315 316 for (int phoneId = 0; phoneId < mNumPhones; phoneId++) { 317 if (newActivePhones.contains(phoneId) == false) { 318 deactivate(phoneId); 319 } 320 } 321 322 // only activate phones up to the limit 323 for (int phoneId : newActivePhones) { 324 activate(phoneId); 325 } 326 } 327 } 328 329 private static class PhoneState { 330 public volatile boolean active = false; 331 public long lastRequested = 0; 332 } 333 334 private void deactivate(int phoneId) { 335 PhoneState state = mPhoneStates[phoneId]; 336 if (state.active == false) return; 337 state.active = false; 338 log("deactivate " + phoneId); 339 state.lastRequested = System.currentTimeMillis(); 340 // Skip ALLOW_DATA for single SIM device 341 if (mNumPhones > 1) { 342 mCommandsInterfaces[phoneId].setDataAllowed(false, null); 343 } 344 mActivePhoneRegistrants[phoneId].notifyRegistrants(); 345 } 346 347 private void activate(int phoneId) { 348 PhoneState state = mPhoneStates[phoneId]; 349 if (state.active == true) return; 350 state.active = true; 351 log("activate " + phoneId); 352 state.lastRequested = System.currentTimeMillis(); 353 // Skip ALLOW_DATA for single SIM device 354 if (mNumPhones > 1) { 355 mCommandsInterfaces[phoneId].setDataAllowed(true, null); 356 } 357 mActivePhoneRegistrants[phoneId].notifyRegistrants(); 358 } 359 360 // used when the modem may have been rebooted and we want to resend 361 // setDataAllowed 362 public void resendDataAllowed(int phoneId) { 363 validatePhoneId(phoneId); 364 Message msg = obtainMessage(EVENT_RESEND_DATA_ALLOWED); 365 msg.arg1 = phoneId; 366 msg.sendToTarget(); 367 } 368 369 private void onResendDataAllowed(Message msg) { 370 final int phoneId = msg.arg1; 371 // Skip ALLOW_DATA for single SIM device 372 if (mNumPhones > 1) { 373 mCommandsInterfaces[phoneId].setDataAllowed(mPhoneStates[phoneId].active, null); 374 } 375 } 376 377 private int phoneIdForRequest(NetworkRequest netRequest) { 378 NetworkSpecifier specifier = netRequest.networkCapabilities.getNetworkSpecifier(); 379 int subId; 380 381 if (specifier == null) { 382 subId = mDefaultDataSubscription; 383 } else if (specifier instanceof StringNetworkSpecifier) { 384 try { 385 subId = Integer.parseInt(((StringNetworkSpecifier) specifier).specifier); 386 } catch (NumberFormatException e) { 387 Rlog.e(LOG_TAG, "NumberFormatException on " 388 + ((StringNetworkSpecifier) specifier).specifier); 389 subId = INVALID_SUBSCRIPTION_ID; 390 } 391 } else { 392 subId = INVALID_SUBSCRIPTION_ID; 393 } 394 395 int phoneId = INVALID_PHONE_INDEX; 396 if (subId == INVALID_SUBSCRIPTION_ID) return phoneId; 397 398 for (int i = 0 ; i < mNumPhones; i++) { 399 if (mPhoneSubscriptions[i] == subId) { 400 phoneId = i; 401 break; 402 } 403 } 404 return phoneId; 405 } 406 407 public boolean isPhoneActive(int phoneId) { 408 validatePhoneId(phoneId); 409 return mPhoneStates[phoneId].active; 410 } 411 412 public void registerForActivePhoneSwitch(int phoneId, Handler h, int what, Object o) { 413 validatePhoneId(phoneId); 414 Registrant r = new Registrant(h, what, o); 415 mActivePhoneRegistrants[phoneId].add(r); 416 r.notifyRegistrant(); 417 } 418 419 public void unregisterForActivePhoneSwitch(int phoneId, Handler h) { 420 validatePhoneId(phoneId); 421 mActivePhoneRegistrants[phoneId].remove(h); 422 } 423 424 private void validatePhoneId(int phoneId) { 425 if (phoneId < 0 || phoneId >= mNumPhones) { 426 throw new IllegalArgumentException("Invalid PhoneId"); 427 } 428 } 429 430 private void log(String l) { 431 Rlog.d(LOG_TAG, l); 432 mLocalLog.log(l); 433 } 434 435 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 436 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 437 pw.println("PhoneSwitcher:"); 438 Calendar c = Calendar.getInstance(); 439 for (int i = 0; i < mNumPhones; i++) { 440 PhoneState ps = mPhoneStates[i]; 441 c.setTimeInMillis(ps.lastRequested); 442 pw.println("PhoneId(" + i + ") active=" + ps.active + ", lastRequest=" + 443 (ps.lastRequested == 0 ? "never" : 444 String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c))); 445 } 446 pw.increaseIndent(); 447 mLocalLog.dump(fd, pw, args); 448 pw.decreaseIndent(); 449 } 450 } 451