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