Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bc2eba90 authored by Robert Greenwalt's avatar Robert Greenwalt
Browse files

DO NOT MERGE - Make PhoneSwitcher class.

Monitors subscription->phone mapping, default subscription setting and
all mobile network requests to prioritize things, activate/deactivate phones
and also notify the curious about which phones are currently active.

Change-Id: I933876a018aebbbcfd86f48451d8b6e47646266e
parent e5e4b949
Loading
Loading
Loading
Loading
+20 −5
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.LocalServerSocket;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -73,9 +75,13 @@ public class PhoneFactory {
    static private boolean sMadeDefaults = false;
    static private PhoneNotifier sPhoneNotifier;
    static private Context sContext;
    //static private PhoneSwitcher mPhoneSwitcher;

    static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>();

    // TODO - make this a dynamic property read from the modem
    public static final int MAX_ACTIVE_PHONES = 1;

    //***** Class Methods

    public static void makeDefaultPhones(Context context) {
@@ -202,11 +208,16 @@ public class PhoneFactory {
                    sPhones[i].startMonitoringImsService();
                }

                SubscriptionMonitor subscriptionMonitor = new SubscriptionMonitor(
                        ITelephonyRegistry.Stub.asInterface(
                        ServiceManager.getService("telephony.registry")),
                        sContext,
                        SubscriptionController.getInstance(), numPhones);
                ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(
                        ServiceManager.getService("telephony.registry"));
                SubscriptionController sc = SubscriptionController.getInstance();

                SubscriptionMonitor subscriptionMonitor = new SubscriptionMonitor(tr,
                        sContext, sc, numPhones);

                //mPhoneSwitcher = new PhoneSwitcher(MAX_ACTIVE_PHONES, numPhones,
                //        sContext, sc, Looper.myLooper(), tr, sCommandsInterfaces,
                //        sPhones);
            }
        }
    }
@@ -452,6 +463,10 @@ public class PhoneFactory {
    public static void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println("PhoneFactory:");
        pw.println(" sMadeDefaults=" + sMadeDefaults);

        //mPhoneSwitcher.dump(fd, pw, args);
        //pw.println();

        Phone[] phones = (Phone[])PhoneFactory.getPhones();
        int i = -1;
        for(Phone phone : phones) {
+391 −0
Original line number Diff line number Diff line
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.android.internal.telephony;

import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkRequest;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.RemoteException;
import android.telephony.Rlog;
import android.text.TextUtils;
import android.util.LocalLog;

import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.dataconnection.DcRequest;
import com.android.internal.util.IndentingPrintWriter;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.IllegalArgumentException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * Utility singleton to monitor subscription changes and incoming NetworkRequests
 * and determine which phone/phones are active.
 *
 * Manages the ALLOW_DATA calls to modems and notifies phones about changes to
 * the active phones.  Note we don't wait for data attach (which may not happen anyway).
 */
public class PhoneSwitcher extends Handler {
    private final static String LOG_TAG = "PhoneSwitcher";
    private final static boolean VDBG = false;

    private final int mMaxActivePhones;
    private final List<DcRequest> mPrioritizedDcRequests = new ArrayList<DcRequest>();
    private final RegistrantList[] mActivePhoneRegistrants;
    private final SubscriptionController mSubscriptionController;
    private final int[] mPhoneSubscriptions;
    private final CommandsInterface[] mCommandsInterfaces;
    private final Context mContext;
    private final PhoneState[] mPhoneStates;
    private final int mNumPhones;
    private final Phone[] mPhones;
    private final LocalLog mLocalLog;

    private int mDefaultDataSubscription;

    private final static int EVENT_DEFAULT_SUBSCRIPTION_CHANGED = 101;
    private final static int EVENT_SUBSCRIPTION_CHANGED         = 102;
    private final static int EVENT_REQUEST_NETWORK              = 103;
    private final static int EVENT_RELEASE_NETWORK              = 104;
    private final static int EVENT_EMERGENCY_TOGGLE             = 105;

    private final static int MAX_LOCAL_LOG_LINES = 30;

    public PhoneSwitcher(int maxActivePhones, int numPhones, Context context,
            SubscriptionController subscriptionController, Looper looper, ITelephonyRegistry tr,
            CommandsInterface[] cis, Phone[] phones) {
        super(looper);
        mContext = context;
        mNumPhones = numPhones;
        mPhones = phones;
        mPhoneSubscriptions = new int[numPhones];
        mMaxActivePhones = maxActivePhones;
        mLocalLog = new LocalLog(MAX_LOCAL_LOG_LINES);

        mSubscriptionController = subscriptionController;

        mActivePhoneRegistrants = new RegistrantList[numPhones];
        mPhoneStates = new PhoneState[numPhones];
        for (int i = 0; i < numPhones; i++) {
            mActivePhoneRegistrants[i] = new RegistrantList();
            mPhoneStates[i] = new PhoneState();
            if (mPhones[i] != null) {
                mPhones[i].registerForEmergencyCallToggle(this, EVENT_EMERGENCY_TOGGLE, null);
            }
        }

        mCommandsInterfaces = cis;

        try {
            tr.addOnSubscriptionsChangedListener("PhoneSwitcher", mSubscriptionsChangedListener);
        } catch (RemoteException e) {
        }

        mContext.registerReceiver(mDefaultDataChangedReceiver,
                new IntentFilter(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED));

        NetworkCapabilities netCap = new NetworkCapabilities();
        netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
        netCap.setNetworkSpecifier(NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);

        NetworkFactory networkFactory = new PhoneSwitcherNetworkRequestListener(looper, context,
                netCap, this);
        networkFactory.setScoreFilter(50);
        networkFactory.register();

        log("PhoneSwitcher started");
    }

    private final BroadcastReceiver mDefaultDataChangedReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Message msg = PhoneSwitcher.this.obtainMessage(EVENT_DEFAULT_SUBSCRIPTION_CHANGED);
            msg.sendToTarget();
        }
    };

    private final IOnSubscriptionsChangedListener mSubscriptionsChangedListener =
            new IOnSubscriptionsChangedListener.Stub() {
        @Override
        public void onSubscriptionsChanged() {
            Message msg = PhoneSwitcher.this.obtainMessage(EVENT_SUBSCRIPTION_CHANGED);
            msg.sendToTarget();
        }
    };

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case EVENT_SUBSCRIPTION_CHANGED: {
                onEvaluate(REQUESTS_UNCHANGED, "subChanged");
                break;
            }
            case EVENT_DEFAULT_SUBSCRIPTION_CHANGED: {
                onEvaluate(REQUESTS_UNCHANGED, "defaultChanged");
                break;
            }
            case EVENT_REQUEST_NETWORK: {
                onRequestNetwork((NetworkRequest)msg.obj);
                break;
            }
            case EVENT_RELEASE_NETWORK: {
                onReleaseNetwork((NetworkRequest)msg.obj);
                break;
            }
            case EVENT_EMERGENCY_TOGGLE: {
                onEvaluate(REQUESTS_CHANGED, "emergencyToggle");
                break;
            }
        }
    }

    private boolean isEmergency() {
        for (Phone p : mPhones) {
            if (p == null) continue;
            if (p.isInEcm() || p.isInEmergencyCall()) return true;
        }
        return false;
    }

    private static class PhoneSwitcherNetworkRequestListener extends NetworkFactory {
        private final PhoneSwitcher mPhoneSwitcher;
        public PhoneSwitcherNetworkRequestListener (Looper l, Context c,
                NetworkCapabilities nc, PhoneSwitcher ps) {
            super(l, c, "PhoneSwitcherNetworkRequstListener", nc);
            mPhoneSwitcher = ps;
        }

        @Override
        protected void needNetworkFor(NetworkRequest networkRequest, int score) {
            Message msg = mPhoneSwitcher.obtainMessage(EVENT_REQUEST_NETWORK);
            msg.obj = networkRequest;
            msg.sendToTarget();
        }

        @Override
        protected void releaseNetworkFor(NetworkRequest networkRequest) {
            Message msg = mPhoneSwitcher.obtainMessage(EVENT_RELEASE_NETWORK);
            msg.obj = networkRequest;
            msg.sendToTarget();
        }
    }

    private void onRequestNetwork(NetworkRequest networkRequest) {
        final DcRequest dcRequest = new DcRequest(networkRequest, mContext);
        if (mPrioritizedDcRequests.contains(dcRequest) == false) {
            mPrioritizedDcRequests.add(dcRequest);
            Collections.sort(mPrioritizedDcRequests);
            onEvaluate(REQUESTS_CHANGED, "netRequest");
        }
    }

    private void onReleaseNetwork(NetworkRequest networkRequest) {
        final DcRequest dcRequest = new DcRequest(networkRequest, mContext);

        if (mPrioritizedDcRequests.remove(dcRequest)) {
            onEvaluate(REQUESTS_CHANGED, "netReleased");
        }
    }

    private static final boolean REQUESTS_CHANGED   = true;
    private static final boolean REQUESTS_UNCHANGED = false;
    /**
     * Re-evaluate things.
     * Do nothing if nothing's changed.
     *
     * Otherwise, go through the requests in priority order adding their phone
     * until we've added up to the max allowed.  Then go through shutting down
     * phones that aren't in the active phone list.  Finally, activate all
     * phones in the active phone list.
     */
    private void onEvaluate(boolean requestsChanged, String why) {
        if (isEmergency()) {
            log("onEvalute aborted due to Emergency");
            return;
        }

        boolean diffDetected = requestsChanged;
        final int dataSub = mSubscriptionController.getDefaultDataSubId();
        if (dataSub != mDefaultDataSubscription) {
            mDefaultDataSubscription = dataSub;
            diffDetected = true;
        }

        for (int i = 0; i < mNumPhones; i++) {
            int sub = mSubscriptionController.getSubIdUsingPhoneId(i);
            if (sub != mPhoneSubscriptions[i]) {
                mPhoneSubscriptions[i] = sub;
                diffDetected = true;
            }
        }

        if (diffDetected) {
            log("evaluating due to " + why);

            List<Integer> newActivePhones = new ArrayList<Integer>();

            for (DcRequest dcRequest : mPrioritizedDcRequests) {
                int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest);
                if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
                if (newActivePhones.contains(phoneIdForRequest)) continue;
                newActivePhones.add(phoneIdForRequest);
                if (newActivePhones.size() >= mMaxActivePhones) break;
            }

            if (VDBG) {
                log("default subId = " + mDefaultDataSubscription);
                for (int i = 0; i < mNumPhones; i++) {
                    log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]");
                }
                log(" newActivePhones:");
                for (Integer i : newActivePhones) log("  " + i);
            }

            for (int phoneId = 0; phoneId < mNumPhones; phoneId++) {
                if (newActivePhones.contains(phoneId) == false) {
                    deactivate(phoneId);
                }
            }

            // only activate phones up to the limit
            for (int phoneId : newActivePhones) {
                activate(phoneId);
            }
        }
    }

    private static class PhoneState {
        public volatile boolean active = false;
        public long lastRequested = 0;
    }

    private void deactivate(int phoneId) {
        PhoneState state = mPhoneStates[phoneId];
        if (state.active == false) return;
        state.active = false;
        log("deactivate " + phoneId);
        state.lastRequested = System.currentTimeMillis();
        mCommandsInterfaces[phoneId].setDataAllowed(false, null);
        mActivePhoneRegistrants[phoneId].notifyRegistrants();
    }

    private void activate(int phoneId) {
        PhoneState state = mPhoneStates[phoneId];
        if (state.active == true) return;
        state.active = true;
        log("activate " + phoneId);
        state.lastRequested = System.currentTimeMillis();
        mCommandsInterfaces[phoneId].setDataAllowed(true, null);
        mActivePhoneRegistrants[phoneId].notifyRegistrants();
    }

    private int phoneIdForRequest(NetworkRequest netRequest) {
        String specifier = netRequest.networkCapabilities.getNetworkSpecifier();
        int subId;

        if (TextUtils.isEmpty(specifier)) {
            subId = mDefaultDataSubscription;
        } else {
            subId = Integer.parseInt(specifier);
        }
        int phoneId = INVALID_PHONE_INDEX;
        if (subId == INVALID_SUBSCRIPTION_ID) return phoneId;

        for (int i = 0 ; i < mNumPhones; i++) {
            if (mPhoneSubscriptions[i] == subId) {
                phoneId = i;
                break;
            }
        }
        return phoneId;
    }

    public boolean isPhoneActive(int phoneId) {
        validatePhoneId(phoneId);
        return mPhoneStates[phoneId].active;
    }

    public void registerForActivePhoneSwitch(int phoneId, Handler h, int what, Object o) {
        validatePhoneId(phoneId);
        Registrant r = new Registrant(h, what, o);
        mActivePhoneRegistrants[phoneId].add(r);
        r.notifyRegistrant();
    }

    public void unregisterForActivePhoneSwitch(int phoneId, Handler h) {
        validatePhoneId(phoneId);
        mActivePhoneRegistrants[phoneId].remove(h);
    }

    private void validatePhoneId(int phoneId) {
        if (phoneId < 0 || phoneId >= mNumPhones) {
            throw new IllegalArgumentException("Invalid PhoneId");
        }
    }

    private void log(String l) {
        Rlog.d(LOG_TAG, l);
        mLocalLog.log(l);
    }

    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
        pw.println("PhoneSwitcher:");
        Calendar c = Calendar.getInstance();
        for (int i = 0; i < mNumPhones; i++) {
            PhoneState ps = mPhoneStates[i];
            c.setTimeInMillis(ps.lastRequested);
            pw.println("PhoneId(" + i + ") active=" + ps.active + ", lastRequest=" +
                    (ps.lastRequested == 0 ? "never" :
                     String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)));
        }
        pw.increaseIndent();
        mLocalLog.dump(fd, pw, args);
        pw.decreaseIndent();
    }
}
+26 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.internal.telephony.dataconnection;

import android.app.PendingIntent;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.NetworkConfig;
import android.telephony.Rlog;
import android.text.TextUtils;
@@ -507,6 +508,31 @@ public class ApnContext {
        return mRetryManager.getInterApnDelay(failFastEnabled);
    }

    public static int apnIdForType(int networkType) {
        switch (networkType) {
        case ConnectivityManager.TYPE_MOBILE:
            return DctConstants.APN_DEFAULT_ID;
        case ConnectivityManager.TYPE_MOBILE_MMS:
            return DctConstants.APN_MMS_ID;
        case ConnectivityManager.TYPE_MOBILE_SUPL:
            return DctConstants.APN_SUPL_ID;
        case ConnectivityManager.TYPE_MOBILE_DUN:
            return DctConstants.APN_DUN_ID;
        case ConnectivityManager.TYPE_MOBILE_FOTA:
            return DctConstants.APN_FOTA_ID;
        case ConnectivityManager.TYPE_MOBILE_IMS:
            return DctConstants.APN_IMS_ID;
        case ConnectivityManager.TYPE_MOBILE_CBS:
            return DctConstants.APN_CBS_ID;
        case ConnectivityManager.TYPE_MOBILE_IA:
            return DctConstants.APN_IA_ID;
        case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
            return DctConstants.APN_EMERGENCY_ID;
        default:
            return DctConstants.APN_INVALID_ID;
        }
    }

    @Override
    public synchronized String toString() {
        // We don't print mDataConnection because its recursive.
+167 −0

File added.

Preview size limit exceeded, changes collapsed.

+19 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.IccOpenLogicalChannelResponse;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
@@ -44,6 +45,7 @@ import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccIoResult;

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.List;

public class SimulatedCommands extends BaseCommands
@@ -128,6 +130,10 @@ public class SimulatedCommands extends BaseCommands
        mPin2Code = DEFAULT_SIM_PIN2_CODE;
    }

    private void log(String str) {
        Rlog.d(LOG_TAG, str);
    }

    //***** CommandsInterface implementation

    @Override
@@ -1923,4 +1929,17 @@ public class SimulatedCommands extends BaseCommands
    public void setOpenChannelId(int channelId) {
        mChannelId = channelId;
    }

    private AtomicBoolean mAllowed = new AtomicBoolean(false);
    @Override
    public void setDataAllowed(boolean allowed, Message result) {
        log("setDataAllowed = " + allowed);
        mAllowed.set(allowed);
        resultSuccess(result, null);
    }

    @VisibleForTesting
    public boolean isDataAllowed() {
        return mAllowed.get();
    }
}
Loading