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