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.MatchAllNetworkSpecifier;
27import android.net.NetworkCapabilities;
28import android.net.NetworkFactory;
29import android.net.NetworkRequest;
30import android.net.NetworkSpecifier;
31import android.net.StringNetworkSpecifier;
32import android.os.Handler;
33import android.os.Looper;
34import android.os.Message;
35import android.os.Registrant;
36import android.os.RegistrantList;
37import android.os.RemoteException;
38import android.telephony.Rlog;
39import android.util.LocalLog;
40
41import com.android.internal.annotations.VisibleForTesting;
42import com.android.internal.telephony.dataconnection.DcRequest;
43import com.android.internal.util.IndentingPrintWriter;
44
45import java.io.FileDescriptor;
46import java.io.PrintWriter;
47import java.util.ArrayList;
48import java.util.Calendar;
49import java.util.Collections;
50import 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 */
59public 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