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