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

Commit 4736e3b6 authored by Xiangyu/Malcolm Chen's avatar Xiangyu/Malcolm Chen Committed by Gerrit Code Review
Browse files

Merge changes from topic "131854492"

* changes:
  Adding subscription group owner.
  Put all operations of MultiSimSettingController in main looper.
parents 38b4107a d980662d
Loading
Loading
Loading
Loading
+117 −17
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;

import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -49,12 +51,17 @@ import java.util.stream.Collectors;
 *    become the new default.
 * 3) For primary subscriptions, only default data subscription will have MOBILE_DATA on.
 */
public class MultiSimSettingController {
public class MultiSimSettingController extends Handler {
    private static final String LOG_TAG = "MultiSimSettingController";
    private static final boolean DBG = true;
    private static final int EVENT_USER_DATA_ENABLED                 = 1;
    private static final int EVENT_ROAMING_DATA_ENABLED              = 2;
    private static final int EVENT_ALL_SUBSCRIPTIONS_LOADED          = 3;
    private static final int EVENT_SUBSCRIPTION_INFO_CHANGED         = 4;
    private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED        = 5;
    private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6;

    private final Context mContext;
    private final Phone[] mPhones;
    private final SubscriptionController mSubController;
    private boolean mIsAllSubscriptionsLoaded;
    private List<SubscriptionInfo> mPrimarySubList;
@@ -68,17 +75,104 @@ public class MultiSimSettingController {
    public static MultiSimSettingController getInstance() {
        synchronized (SubscriptionController.class) {
            if (sInstance == null) {
                sInstance = new MultiSimSettingController();
                Log.wtf(LOG_TAG, "getInstance null");
            }

            return sInstance;
        }
    }

    /**
     * Init instance of MultiSimSettingController.
     */
    public static MultiSimSettingController init(Context context, SubscriptionController sc) {
        synchronized (SubscriptionController.class) {
            if (sInstance == null) {
                sInstance = new MultiSimSettingController(context, sc);
            } else {
                Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
            }
            return sInstance;
        }
    }

    @VisibleForTesting
    public MultiSimSettingController() {
        mContext = PhoneFactory.getDefaultPhone().getContext();
        mPhones = PhoneFactory.getPhones();
        mSubController = SubscriptionController.getInstance();
    public MultiSimSettingController(Context context, SubscriptionController sc) {
        mContext = context;
        mSubController = sc;
    }

    /**
     * Notify MOBILE_DATA of a subscription is changed.
     */
    public void notifyUserDataEnabled(int subId, boolean enable) {
        obtainMessage(EVENT_USER_DATA_ENABLED, subId, enable ? 1 : 0).sendToTarget();
    }

    /**
     * Notify DATA_ROAMING of a subscription is changed.
     */
    public void notifyRoamingDataEnabled(int subId, boolean enable) {
        obtainMessage(EVENT_ROAMING_DATA_ENABLED, subId, enable ? 1 : 0).sendToTarget();
    }

    /**
     * Notify that, for the first time after boot, SIMs are all loaded
     */
    public void notifyAllSubscriptionLoaded() {
        obtainMessage(EVENT_ALL_SUBSCRIPTIONS_LOADED).sendToTarget();
    }

    /**
     * Notify subscription info change.
     */
    public void notifySubscriptionInfoChanged() {
        obtainMessage(EVENT_SUBSCRIPTION_INFO_CHANGED).sendToTarget();
    }

    /**
     * Notify subscription group information change.
     */
    public void notifySubscriptionGroupChanged(ParcelUuid groupUuid) {
        obtainMessage(EVENT_SUBSCRIPTION_GROUP_CHANGED, groupUuid).sendToTarget();
    }

    /**
     * Notify default data subscription change.
     */
    public void notifyDefaultDataSubChanged() {
        obtainMessage(EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED).sendToTarget();
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case EVENT_USER_DATA_ENABLED: {
                int subId = msg.arg1;
                boolean enable = msg.arg2 != 0;
                onUserDataEnabled(subId, enable);
                break;
            }
            case EVENT_ROAMING_DATA_ENABLED: {
                int subId = msg.arg1;
                boolean enable = msg.arg2 != 0;
                onRoamingDataEnabled(subId, enable);
                break;
            }
            case EVENT_ALL_SUBSCRIPTIONS_LOADED:
                onAllSubscriptionsLoaded();
                break;
            case EVENT_SUBSCRIPTION_INFO_CHANGED:
                onSubscriptionsChanged();
                break;
            case EVENT_SUBSCRIPTION_GROUP_CHANGED:
                ParcelUuid groupUuid = (ParcelUuid) msg.obj;
                onSubscriptionGroupChanged(groupUuid);
                break;
            case EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
                onDefaultDataSettingChanged();
                break;
        }
    }

    /**
@@ -87,7 +181,8 @@ public class MultiSimSettingController {
     * If user is enabling a non-default non-opportunistic subscription, make it default
     * data subscription.
     */
    public synchronized void onUserDataEnabled(int subId, boolean enable) {
    @VisibleForTesting
    public void onUserDataEnabled(int subId, boolean enable) {
        if (DBG) log("onUserDataEnabled");
        // Make sure MOBILE_DATA of subscriptions in same group are synced.
        setUserDataEnabledForGroup(subId, enable);
@@ -102,7 +197,8 @@ public class MultiSimSettingController {
    /**
     * Make sure DATA_ROAMING of subscriptions in same group are synced.
     */
    public synchronized void onRoamingDataEnabled(int subId, boolean enable) {
    @VisibleForTesting
    public void onRoamingDataEnabled(int subId, boolean enable) {
        if (DBG) log("onRoamingDataEnabled");
        setRoamingDataEnabledForGroup(subId, enable);

@@ -113,7 +209,8 @@ public class MultiSimSettingController {
    /**
     * Mark mIsAllSubscriptionsLoaded and update defaults and mobile data enabling.
     */
    public synchronized void onAllSubscriptionsLoaded() {
    @VisibleForTesting
    public void onAllSubscriptionsLoaded() {
        if (DBG) log("onAllSubscriptionsLoaded");
        mIsAllSubscriptionsLoaded = true;
        updateDefaults();
@@ -125,7 +222,8 @@ public class MultiSimSettingController {
     *
     * Make sure non-default non-opportunistic subscriptions has data off.
     */
    public synchronized void onSubscriptionsChanged() {
    @VisibleForTesting
    public void onSubscriptionsChanged() {
        if (DBG) log("onSubscriptionsChanged");
        if (!mIsAllSubscriptionsLoaded) return;
        updateDefaults();
@@ -135,7 +233,8 @@ public class MultiSimSettingController {
    /**
     * Make sure non-default non-opportunistic subscriptions has data disabled.
     */
    public synchronized void onDefaultDataSettingChanged() {
    @VisibleForTesting
    public void onDefaultDataSettingChanged() {
        if (DBG) log("onDefaultDataSettingChanged");
        disableDataForNonDefaultNonOpportunisticSubscriptions();
    }
@@ -146,7 +245,8 @@ public class MultiSimSettingController {
     * TODO: b/130258159 have a separate database table for grouped subscriptions so we don't
     * manually sync each setting.
     */
    public synchronized void onSubscriptionGroupChanged(ParcelUuid groupUuid) {
    @VisibleForTesting
    public void onSubscriptionGroupChanged(ParcelUuid groupUuid) {
        if (DBG) log("onSubscriptionGroupChanged");

        List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup(
@@ -207,7 +307,7 @@ public class MultiSimSettingController {
     * 4) If non above is met, clear the default value to INVALID.
     */
    @VisibleForTesting
    public synchronized void updateDefaults() {
    public void updateDefaults() {
        if (DBG) log("updateDefaults");

        if (!mIsAllSubscriptionsLoaded) return;
@@ -306,7 +406,7 @@ public class MultiSimSettingController {
            return;
        }

        for (Phone phone : mPhones) {
        for (Phone phone : PhoneFactory.getPhones()) {
            if (phone.getSubId() != defaultDataSub
                    && SubscriptionManager.isValidSubscriptionId(phone.getSubId())
                    && !mSubController.isOpportunistic(phone.getSubId())
@@ -321,7 +421,7 @@ public class MultiSimSettingController {
     * Make sure MOBILE_DATA of subscriptions in the same group with the subId
     * are synced.
     */
    private synchronized void setUserDataEnabledForGroup(int subId, boolean enable) {
    private void setUserDataEnabledForGroup(int subId, boolean enable) {
        log("setUserDataEnabledForGroup subId " + subId + " enable " + enable);
        List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup(
                mSubController.getGroupUuid(subId), mContext.getOpPackageName());
@@ -360,7 +460,7 @@ public class MultiSimSettingController {
     * Make sure DATA_ROAMING of subscriptions in the same group with the subId
     * are synced.
     */
    private synchronized void setRoamingDataEnabledForGroup(int subId, boolean enable) {
    private void setRoamingDataEnabledForGroup(int subId, boolean enable) {
        SubscriptionController subController = SubscriptionController.getInstance();
        List<SubscriptionInfo> infoList = subController.getSubscriptionsInGroup(
                mSubController.getGroupUuid(subId), mContext.getOpPackageName());
+1 −0
Original line number Diff line number Diff line
@@ -173,6 +173,7 @@ public class PhoneFactory {

                Rlog.i(LOG_TAG, "Creating SubscriptionController");
                SubscriptionController.init(context, sCommandsInterfaces);
                MultiSimSettingController.init(context, SubscriptionController.getInstance());

                if (context.getPackageManager().hasSystemFeature(
                        PackageManager.FEATURE_TELEPHONY_EUICC)) {
+77 −18
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.util.ArrayUtils;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -307,7 +308,7 @@ public class SubscriptionController extends ISub.Stub {
        // FIXME: Remove if listener technique accepted.
        broadcastSimInfoContentChanged();

        MultiSimSettingController.getInstance().onSubscriptionsChanged();
        MultiSimSettingController.getInstance().notifySubscriptionInfoChanged();
        TelephonyMetrics metrics = TelephonyMetrics.getInstance();
        List<SubscriptionInfo> subInfos;
        synchronized (mSubInfoListLock) {
@@ -381,6 +382,8 @@ public class SubscriptionController extends ISub.Stub {
                SubscriptionManager.PROFILE_CLASS));
        int subType = cursor.getInt(cursor.getColumnIndexOrThrow(
                SubscriptionManager.SUBSCRIPTION_TYPE));
        String groupOwner = getOptionalStringFromCursor(cursor, SubscriptionManager.GROUP_OWNER,
                /*defaultVal*/ null);

        if (VDBG) {
            String iccIdToPrint = SubscriptionInfo.givePrintableIccid(iccId);
@@ -404,11 +407,18 @@ public class SubscriptionController extends ISub.Stub {
        SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
                carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc,
                countryIso, isEmbedded, accessRules, cardId, publicCardId, isOpportunistic,
                groupUUID, false /* isGroupDisabled */, carrierId, profileClass, subType);
                groupUUID, false /* isGroupDisabled */, carrierId, profileClass, subType,
                groupOwner);
        info.setAssociatedPlmns(ehplmns, hplmns);
        return info;
    }

    private String getOptionalStringFromCursor(Cursor cursor, String column, String defaultVal) {
        // Return defaultVal if the column doesn't exist.
        int columnIndex = cursor.getColumnIndex(column);
        return (columnIndex == -1) ? defaultVal : cursor.getString(columnIndex);
    }

    /**
     * Query SubInfoRecord(s) from subinfo database
     * @param selection A filter declaring which rows to return
@@ -2210,7 +2220,7 @@ public class SubscriptionController extends ISub.Stub {
            int previousDefaultSub = getDefaultSubId();
            Settings.Global.putInt(mContext.getContentResolver(),
                    Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId);
            MultiSimSettingController.getInstance().onDefaultDataSettingChanged();
            MultiSimSettingController.getInstance().notifyDefaultDataSubChanged();
            broadcastDefaultDataSubIdChanged(subId);
            if (previousDefaultSub != getDefaultSubId()) {
                sendDefaultChangedBroadcast(getDefaultSubId());
@@ -2878,6 +2888,7 @@ public class SubscriptionController extends ISub.Stub {

            ContentValues value = new ContentValues();
            value.put(SubscriptionManager.GROUP_UUID, groupUUID.toString());
            value.put(SubscriptionManager.GROUP_OWNER, callingPackage);
            int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
                    value, getSelectionForSubIdList(subIdList), null);

@@ -2887,7 +2898,7 @@ public class SubscriptionController extends ISub.Stub {

            notifySubscriptionInfoChanged();

            MultiSimSettingController.getInstance().onSubscriptionGroupChanged(groupUUID);
            MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUUID);

            return groupUUID;
        } finally {
@@ -2895,14 +2906,31 @@ public class SubscriptionController extends ISub.Stub {
        }
    }

    // TODO: when having a group owner or packageName for subscription, use that to check.
    // Currently for this method to return false, all subscriptions in the group needs to have
    // carrier privilege rules loaded, which means active or available.
    private boolean canPackageManageGroup(ParcelUuid groupUuid, String callingPackage) {
    private String getOwnerPackageOfSubGroup(ParcelUuid groupUuid) {
        if (groupUuid == null) return null;

        List<SubscriptionInfo> infoList = getSubInfo(SubscriptionManager.GROUP_UUID
                + "=\'" + groupUuid.toString() + "\'", null);

        return ArrayUtils.isEmpty(infoList) ? null : infoList.get(0).getGroupOwner();
    }

    /**
     *
     * @param groupUuid a UUID assigned to the subscription group.
     * @param callingPackage the package making the IPC.
     * @return if callingPackage has carrier privilege on sublist.
     *
     */
    public boolean canPackageManageGroup(ParcelUuid groupUuid, String callingPackage) {
        if (groupUuid == null) {
            throw new IllegalArgumentException("Invalid groupUuid");
        }

        if (TextUtils.isEmpty(callingPackage)) {
            throw new IllegalArgumentException("Empty callingPackage");
        }

        List<SubscriptionInfo> infoList;

        // Getting all subscriptions in the group.
@@ -2914,13 +2942,36 @@ public class SubscriptionController extends ISub.Stub {
            Binder.restoreCallingIdentity(identity);
        }

        if (infoList == null || infoList.isEmpty()) {
        if (ArrayUtils.isEmpty(infoList)) {
            throw new IllegalArgumentException("No subscription in group " + groupUuid);
        }

        // If the calling package is the group owner, skip carrier permission check and return
        // true as it was done before.
        if (callingPackage.equals(infoList.get(0).getGroupOwner())) return true;

        // Check carrier privilege for all subscriptions in the group.
        int[] subIdArray = infoList.stream().mapToInt(info -> info.getSubscriptionId()).toArray();
        return checkCarrierPrivilegeOnSubList(subIdArray, callingPackage);
        int[] subIdArray = infoList.stream().mapToInt(info -> info.getSubscriptionId())
                .toArray();
        return (checkCarrierPrivilegeOnSubList(subIdArray, callingPackage));
    }

    private int updateGroupOwner(ParcelUuid groupUuid, String groupOwner) {
        // If the existing group owner is different from current caller, make caller the new
        // owner of all subscriptions in group.
        // This is for use-case of:
        // 1) Both package1 and package2 has permission (MODIFY_PHONE_STATE or carrier
        // privilege permission) of all related subscriptions.
        // 2) Package 1 created a group.
        // 3) Package 2 wants to add a subscription into it.
        // Step 3 should be granted as all operations are permission based. Which means as
        // long as the package passes the permission check, it can modify the subscription
        // and the group. And package 2 becomes the new group owner as it's the last to pass
        // permission checks on all members.
        ContentValues value = new ContentValues(1);
        value.put(SubscriptionManager.GROUP_OWNER, groupOwner);
        return mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
                value, SubscriptionManager.GROUP_UUID + "=\"" + groupUuid + "\"", null);
    }

    @Override
@@ -2934,6 +2985,8 @@ public class SubscriptionController extends ISub.Stub {
            throw new IllegalArgumentException("Invalid groupUuid");
        }

        // Makes sure calling package matches caller UID.
        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
        // If it doesn't have modify phone state permission, or carrier privilege permission,
        // a SecurityException will be thrown.
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -2959,11 +3012,11 @@ public class SubscriptionController extends ISub.Stub {
            if (DBG) logdl("addSubscriptionsIntoGroup update DB result: " + result);

            if (result > 0) {
                updateGroupOwner(groupUuid, callingPackage);
                refreshCachedActiveSubscriptionInfoList();
                notifySubscriptionInfoChanged();
                MultiSimSettingController.getInstance().onSubscriptionGroupChanged(groupUuid);
                MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUuid);
            }

        } finally {
            Binder.restoreCallingIdentity(identity);
        }
@@ -2991,12 +3044,15 @@ public class SubscriptionController extends ISub.Stub {
        if (subIdList == null || subIdList.length == 0) {
            return;
        }

        // Makes sure calling package matches caller UID.
        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
        // If it doesn't have modify phone state permission, or carrier privilege permission,
        // a SecurityException will be thrown. If it's due to invalid parameter or internal state,
        // it will return null.
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
                != PERMISSION_GRANTED && !checkCarrierPrivilegeOnSubList(
                subIdList, callingPackage)) {
                != PERMISSION_GRANTED && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage)
                && canPackageManageGroup(groupUuid, callingPackage))) {
            throw new SecurityException("removeSubscriptionsFromGroup needs MODIFY_PHONE_STATE or"
                    + " carrier privilege permission on all specified subscriptions");
        }
@@ -3014,14 +3070,17 @@ public class SubscriptionController extends ISub.Stub {
            }
            ContentValues value = new ContentValues();
            value.put(SubscriptionManager.GROUP_UUID, (String) null);
            value.put(SubscriptionManager.GROUP_OWNER, (String) null);
            int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
                    value, getSelectionForSubIdList(subIdList), null);

            if (DBG) logdl("removeSubscriptionsFromGroup update DB result: " + result);

            if (result > 0) {
                updateGroupOwner(groupUuid, callingPackage);
                refreshCachedActiveSubscriptionInfoList();

                notifySubscriptionInfoChanged();
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
+10 −6
Original line number Diff line number Diff line
@@ -623,7 +623,7 @@ public class SubscriptionInfoUpdater extends Handler {
                        .forEach(cardId -> updateEmbeddedSubscriptions(cardId));
            }
            // update default subId
            MultiSimSettingController.getInstance().onAllSubscriptionsLoaded();
            MultiSimSettingController.getInstance().notifyAllSubscriptionLoaded();
            // broadcast default subId
            SubscriptionController.getInstance().sendDefaultChangedBroadcast(
                    SubscriptionManager.getDefaultSubscriptionId());
@@ -858,19 +858,23 @@ public class SubscriptionInfoUpdater extends Handler {

        String groupUuidString =
                config.getString(CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, "");
        ParcelUuid groupId = null;
        ParcelUuid groupUuid = null;
        if (!TextUtils.isEmpty(groupUuidString)) {
            try {
                // Update via a UUID Structure to ensure consistent formatting
                ParcelUuid groupUuid = ParcelUuid.fromString(groupUuidString);
                groupUuid = ParcelUuid.fromString(groupUuidString);
                if (groupUuid.equals(REMOVE_GROUP_UUID)
                            && currentSubInfo.getGroupUuid() != null) {
                    cv.put(SubscriptionManager.GROUP_UUID, (String) null);
                    if (DBG) logd("Group Removed for" + currentSubId);
                } else {
                    // TODO: validate and update group owner information once feasible.
                } else if (SubscriptionController.getInstance().canPackageManageGroup(groupUuid,
                        configPackageName)) {
                    cv.put(SubscriptionManager.GROUP_UUID, groupUuid.toString());
                    cv.put(SubscriptionManager.GROUP_OWNER, configPackageName);
                    if (DBG) logd("Group Added for" + currentSubId);
                } else {
                    loge("configPackageName " + configPackageName + " doesn't own grouUuid "
                            + groupUuid);
                }
            } catch (IllegalArgumentException e) {
                loge("Invalid Group UUID=" + groupUuidString);
@@ -880,7 +884,7 @@ public class SubscriptionInfoUpdater extends Handler {
                    .getUriForSubscriptionId(currentSubId), cv, null, null) > 0) {
            sc.refreshCachedActiveSubscriptionInfoList();
            sc.notifySubscriptionInfoChanged();
            MultiSimSettingController.getInstance().onSubscriptionGroupChanged(groupId);
            MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUuid);
        }
    }

+4 −3
Original line number Diff line number Diff line
@@ -139,7 +139,8 @@ public class DataEnabledSettings {
        if (changed) {
            mPhone.notifyUserMobileDataStateChanged(enabled);
            updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
            MultiSimSettingController.getInstance().onUserDataEnabled(mPhone.getSubId(), enabled);
            MultiSimSettingController.getInstance().notifyUserDataEnabled(mPhone.getSubId(),
                    enabled);
        }
    }

@@ -241,8 +242,8 @@ public class DataEnabledSettings {
                Settings.Global.DATA_ROAMING, mPhone.getSubId(), enabled);

        if (changed) {
            MultiSimSettingController.getInstance()
                    .onRoamingDataEnabled(mPhone.getSubId(), enabled);
            MultiSimSettingController.getInstance().notifyRoamingDataEnabled(mPhone.getSubId(),
                    enabled);
        }
    }

Loading