Home | History | Annotate | Download | only in telephony
      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