PhoneSwitcher.java revision 87c2958ce9e74b35ca6ef36f0b5caaaac8d5b008
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        networkFactory.setScoreFilter(50);
156        networkFactory.register();
157
158        log("PhoneSwitcher started");
159    }
160
161    private final BroadcastReceiver mDefaultDataChangedReceiver = new BroadcastReceiver() {
162        @Override
163        public void onReceive(Context context, Intent intent) {
164            Message msg = PhoneSwitcher.this.obtainMessage(EVENT_DEFAULT_SUBSCRIPTION_CHANGED);
165            msg.sendToTarget();
166        }
167    };
168
169    private final IOnSubscriptionsChangedListener mSubscriptionsChangedListener =
170            new IOnSubscriptionsChangedListener.Stub() {
171        @Override
172        public void onSubscriptionsChanged() {
173            Message msg = PhoneSwitcher.this.obtainMessage(EVENT_SUBSCRIPTION_CHANGED);
174            msg.sendToTarget();
175        }
176    };
177
178    @Override
179    public void handleMessage(Message msg) {
180        switch (msg.what) {
181            case EVENT_SUBSCRIPTION_CHANGED: {
182                onEvaluate(REQUESTS_UNCHANGED, "subChanged");
183                break;
184            }
185            case EVENT_DEFAULT_SUBSCRIPTION_CHANGED: {
186                onEvaluate(REQUESTS_UNCHANGED, "defaultChanged");
187                break;
188            }
189            case EVENT_REQUEST_NETWORK: {
190                onRequestNetwork((NetworkRequest)msg.obj);
191                break;
192            }
193            case EVENT_RELEASE_NETWORK: {
194                onReleaseNetwork((NetworkRequest)msg.obj);
195                break;
196            }
197            case EVENT_EMERGENCY_TOGGLE: {
198                onEvaluate(REQUESTS_CHANGED, "emergencyToggle");
199                break;
200            }
201            case EVENT_RESEND_DATA_ALLOWED: {
202                onResendDataAllowed(msg);
203                break;
204            }
205        }
206    }
207
208    private boolean isEmergency() {
209        for (Phone p : mPhones) {
210            if (p == null) continue;
211            if (p.isInEcm() || p.isInEmergencyCall()) return true;
212        }
213        return false;
214    }
215
216    private static class PhoneSwitcherNetworkRequestListener extends NetworkFactory {
217        private final PhoneSwitcher mPhoneSwitcher;
218        public PhoneSwitcherNetworkRequestListener (Looper l, Context c,
219                NetworkCapabilities nc, PhoneSwitcher ps) {
220            super(l, c, "PhoneSwitcherNetworkRequstListener", nc);
221            mPhoneSwitcher = ps;
222        }
223
224        @Override
225        protected void needNetworkFor(NetworkRequest networkRequest, int 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            Message msg = mPhoneSwitcher.obtainMessage(EVENT_RELEASE_NETWORK);
234            msg.obj = networkRequest;
235            msg.sendToTarget();
236        }
237    }
238
239    private void onRequestNetwork(NetworkRequest networkRequest) {
240        final DcRequest dcRequest = new DcRequest(networkRequest, mContext);
241        if (mPrioritizedDcRequests.contains(dcRequest) == false) {
242            mPrioritizedDcRequests.add(dcRequest);
243            Collections.sort(mPrioritizedDcRequests);
244            onEvaluate(REQUESTS_CHANGED, "netRequest");
245        }
246    }
247
248    private void onReleaseNetwork(NetworkRequest networkRequest) {
249        final DcRequest dcRequest = new DcRequest(networkRequest, mContext);
250
251        if (mPrioritizedDcRequests.remove(dcRequest)) {
252            onEvaluate(REQUESTS_CHANGED, "netReleased");
253        }
254    }
255
256    private static final boolean REQUESTS_CHANGED   = true;
257    private static final boolean REQUESTS_UNCHANGED = false;
258    /**
259     * Re-evaluate things.
260     * Do nothing if nothing's changed.
261     *
262     * Otherwise, go through the requests in priority order adding their phone
263     * until we've added up to the max allowed.  Then go through shutting down
264     * phones that aren't in the active phone list.  Finally, activate all
265     * phones in the active phone list.
266     */
267    private void onEvaluate(boolean requestsChanged, String reason) {
268        StringBuilder sb = new StringBuilder(reason);
269        if (isEmergency()) {
270            log("onEvalute aborted due to Emergency");
271            return;
272        }
273
274        boolean diffDetected = requestsChanged;
275        final int dataSub = mSubscriptionController.getDefaultDataSubId();
276        if (dataSub != mDefaultDataSubscription) {
277            sb.append(" default ").append(mDefaultDataSubscription).append("->").append(dataSub);
278            mDefaultDataSubscription = dataSub;
279            diffDetected = true;
280
281        }
282
283        for (int i = 0; i < mNumPhones; i++) {
284            int sub = mSubscriptionController.getSubIdUsingPhoneId(i);
285            if (sub != mPhoneSubscriptions[i]) {
286                sb.append(" phone[").append(i).append("] ").append(mPhoneSubscriptions[i]);
287                sb.append("->").append(sub);
288                mPhoneSubscriptions[i] = sub;
289                diffDetected = true;
290            }
291        }
292
293        if (diffDetected) {
294            log("evaluating due to " + sb.toString());
295
296            List<Integer> newActivePhones = new ArrayList<Integer>();
297
298            for (DcRequest dcRequest : mPrioritizedDcRequests) {
299                int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest);
300                if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
301                if (newActivePhones.contains(phoneIdForRequest)) continue;
302                newActivePhones.add(phoneIdForRequest);
303                if (newActivePhones.size() >= mMaxActivePhones) break;
304            }
305
306            if (VDBG) {
307                log("default subId = " + mDefaultDataSubscription);
308                for (int i = 0; i < mNumPhones; i++) {
309                    log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]");
310                }
311                log(" newActivePhones:");
312                for (Integer i : newActivePhones) log("  " + i);
313            }
314
315            for (int phoneId = 0; phoneId < mNumPhones; phoneId++) {
316                if (newActivePhones.contains(phoneId) == false) {
317                    deactivate(phoneId);
318                }
319            }
320
321            // only activate phones up to the limit
322            for (int phoneId : newActivePhones) {
323                activate(phoneId);
324            }
325        }
326    }
327
328    private static class PhoneState {
329        public volatile boolean active = false;
330        public long lastRequested = 0;
331    }
332
333    private void deactivate(int phoneId) {
334        PhoneState state = mPhoneStates[phoneId];
335        if (state.active == false) return;
336        state.active = false;
337        log("deactivate " + phoneId);
338        state.lastRequested = System.currentTimeMillis();
339        mCommandsInterfaces[phoneId].setDataAllowed(false, null);
340        mActivePhoneRegistrants[phoneId].notifyRegistrants();
341    }
342
343    private void activate(int phoneId) {
344        PhoneState state = mPhoneStates[phoneId];
345        if (state.active == true) return;
346        state.active = true;
347        log("activate " + phoneId);
348        state.lastRequested = System.currentTimeMillis();
349        mCommandsInterfaces[phoneId].setDataAllowed(true, null);
350        mActivePhoneRegistrants[phoneId].notifyRegistrants();
351    }
352
353    // used when the modem may have been rebooted and we want to resend
354    // setDataAllowed
355    public void resendDataAllowed(int phoneId) {
356        validatePhoneId(phoneId);
357        Message msg = obtainMessage(EVENT_RESEND_DATA_ALLOWED);
358        msg.arg1 = phoneId;
359        msg.sendToTarget();
360    }
361
362    private void onResendDataAllowed(Message msg) {
363        final int phoneId = msg.arg1;
364        mCommandsInterfaces[phoneId].setDataAllowed(mPhoneStates[phoneId].active, null);
365    }
366
367    private int phoneIdForRequest(NetworkRequest netRequest) {
368        String specifier = netRequest.networkCapabilities.getNetworkSpecifier();
369        int subId;
370
371        if (TextUtils.isEmpty(specifier)) {
372            subId = mDefaultDataSubscription;
373        } else {
374            subId = Integer.parseInt(specifier);
375        }
376        int phoneId = INVALID_PHONE_INDEX;
377        if (subId == INVALID_SUBSCRIPTION_ID) return phoneId;
378
379        for (int i = 0 ; i < mNumPhones; i++) {
380            if (mPhoneSubscriptions[i] == subId) {
381                phoneId = i;
382                break;
383            }
384        }
385        return phoneId;
386    }
387
388    public boolean isPhoneActive(int phoneId) {
389        validatePhoneId(phoneId);
390        return mPhoneStates[phoneId].active;
391    }
392
393    public void registerForActivePhoneSwitch(int phoneId, Handler h, int what, Object o) {
394        validatePhoneId(phoneId);
395        Registrant r = new Registrant(h, what, o);
396        mActivePhoneRegistrants[phoneId].add(r);
397        r.notifyRegistrant();
398    }
399
400    public void unregisterForActivePhoneSwitch(int phoneId, Handler h) {
401        validatePhoneId(phoneId);
402        mActivePhoneRegistrants[phoneId].remove(h);
403    }
404
405    private void validatePhoneId(int phoneId) {
406        if (phoneId < 0 || phoneId >= mNumPhones) {
407            throw new IllegalArgumentException("Invalid PhoneId");
408        }
409    }
410
411    private void log(String l) {
412        Rlog.d(LOG_TAG, l);
413        mLocalLog.log(l);
414    }
415
416    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
417        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
418        pw.println("PhoneSwitcher:");
419        Calendar c = Calendar.getInstance();
420        for (int i = 0; i < mNumPhones; i++) {
421            PhoneState ps = mPhoneStates[i];
422            c.setTimeInMillis(ps.lastRequested);
423            pw.println("PhoneId(" + i + ") active=" + ps.active + ", lastRequest=" +
424                    (ps.lastRequested == 0 ? "never" :
425                     String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)));
426        }
427        pw.increaseIndent();
428        mLocalLog.dump(fd, pw, args);
429        pw.decreaseIndent();
430    }
431}
432