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

Commit 80c9051e authored by Malcolm Chen's avatar Malcolm Chen
Browse files

SubscriptionController notifies opportunistic subscription change.

Implement the feature that when opportunistic subscription is
added, removed or changed, SubscriptionController trigger
notification to all subscribers.

Bug: 115541873 92796390
Test: manual with test app
Change-Id: Ib9b025704b547dcce94d427cce28dbc24fa49f17
Merged-In: Ib9b025704b547dcce94d427cce28dbc24fa49f17
parent 918640bd
Loading
Loading
Loading
Loading
+87 −50
Original line number Diff line number Diff line
@@ -90,9 +90,15 @@ public class SubscriptionController extends ISub.Stub {
    private static final int DEPRECATED_SETTING = -1;
    private ScLocalLog mLocalLog = new ScLocalLog(MAX_LOCAL_LOG_LINES);

    // Lock that both mCacheActiveSubInfoList and mCacheOpportunisticSubInfoList use.
    private Object mSubInfoListLock = new Object();

    /* The Cache of Active SubInfoRecord(s) list of currently in use SubInfoRecord(s) */
    private final List<SubscriptionInfo> mCacheActiveSubInfoList = new ArrayList<>();

    /* Similar to mCacheActiveSubInfoList but only caching opportunistic subscriptions. */
    private List<SubscriptionInfo> mCacheOpportunisticSubInfoList = new ArrayList<>();

    /**
     * Copied from android.util.LocalLog with flush() adding flush and line number
     * TODO: Update LocalLog
@@ -598,39 +604,7 @@ public class SubscriptionController extends ISub.Stub {
     */
    @Override
    public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) {
        if (!isSubInfoReady()) {
            if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
            return null;
        }

        boolean canReadAllPhoneState;
        try {
            canReadAllPhoneState = TelephonyPermissions.checkReadPhoneState(mContext,
                    SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
                    Binder.getCallingUid(), callingPackage, "getActiveSubscriptionInfoList");
        } catch (SecurityException e) {
            canReadAllPhoneState = false;
        }

        synchronized (mCacheActiveSubInfoList) {
            // If the caller can read all phone state, just return the full list.
            if (canReadAllPhoneState) {
                return new ArrayList<>(mCacheActiveSubInfoList);
            }

            // Filter the list to only include subscriptions which the caller can manage.
            return mCacheActiveSubInfoList.stream()
                    .filter(subscriptionInfo -> {
                        try {
                            return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
                                    subscriptionInfo.getSubscriptionId(), callingPackage,
                                    "getActiveSubscriptionInfoList");
                        } catch (SecurityException e) {
                            return false;
                        }
                    })
                    .collect(Collectors.toList());
        }
        return getSubscriptionInfoListFromCacheHelper(callingPackage, mCacheActiveSubInfoList);
    }

    /**
@@ -646,7 +620,9 @@ public class SubscriptionController extends ISub.Stub {
            return;
        }

        synchronized (mCacheActiveSubInfoList) {
        boolean opptSubListChanged = false;

        synchronized (mSubInfoListLock) {
            mCacheActiveSubInfoList.clear();
            List<SubscriptionInfo> activeSubscriptionInfoList = getSubInfo(
                    SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
@@ -654,6 +630,10 @@ public class SubscriptionController extends ISub.Stub {
                activeSubscriptionInfoList.sort(SUBSCRIPTION_INFO_COMPARATOR);
                mCacheActiveSubInfoList.addAll(activeSubscriptionInfoList);
            }

            // Refresh cached opportunistic sub list and detect whether it's changed.
            opptSubListChanged = refreshCachedOpportunisticSubscriptionInfoList();

            if (DBG_CACHE) {
                if (!mCacheActiveSubInfoList.isEmpty()) {
                    for (SubscriptionInfo si : mCacheActiveSubInfoList) {
@@ -665,6 +645,11 @@ public class SubscriptionController extends ISub.Stub {
                }
            }
        }

        // Send notification outside synchronization.
        if (opptSubListChanged) {
            notifyOpportunisticSubscriptionInfoChanged();
        }
    }

    /**
@@ -2308,29 +2293,46 @@ public class SubscriptionController extends ISub.Stub {

    @Override
    public List<SubscriptionInfo> getOpportunisticSubscriptions(int slotId, String callingPackage) {
        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
                SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
                "getOpportunisticSubscriptions")) {
            return null;
        return getSubscriptionInfoListFromCacheHelper(
                callingPackage, mCacheOpportunisticSubInfoList);
    }

        final long token = Binder.clearCallingIdentity();
    // Helper function of getOpportunisticSubscriptions and getActiveSubscriptionInfoList.
    // They are doing similar things except operating on different cache.
    private List<SubscriptionInfo> getSubscriptionInfoListFromCacheHelper(
            String callingPackage, List<SubscriptionInfo> cacheSubList) {
        if (!isSubInfoReady()) {
            if (DBG) logdl("[getSubscriptionInfoList] Sub Controller not ready");
            return null;
        }

        boolean canReadAllPhoneState;
        try {
            List<SubscriptionInfo> activeSubList = getActiveSubscriptionInfoList(callingPackage);
            List<SubscriptionInfo> opportunisticSubList = new ArrayList<>();

            if (activeSubList != null) {
                for (SubscriptionInfo subInfo : activeSubList) {
                    if (subInfo.isOpportunistic() && subInfo.getSimSlotIndex() == slotId) {
                        opportunisticSubList.add(subInfo);
                    }
            canReadAllPhoneState = TelephonyPermissions.checkReadPhoneState(mContext,
                    SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
                    Binder.getCallingUid(), callingPackage, "getSubscriptionInfoList");
        } catch (SecurityException e) {
            canReadAllPhoneState = false;
        }

        synchronized (mSubInfoListLock) {
            // If the caller can read all phone state, just return the full list.
            if (canReadAllPhoneState) {
                return new ArrayList<>(cacheSubList);
            }

            return opportunisticSubList;
        } finally {
            Binder.restoreCallingIdentity(token);
            // Filter the list to only include subscriptions which the caller can manage.
            return cacheSubList.stream()
                    .filter(subscriptionInfo -> {
                        try {
                            return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
                                    subscriptionInfo.getSubscriptionId(), callingPackage,
                                    "getOpportunisticSubscriptions");
                        } catch (SecurityException e) {
                            return false;
                        }
                    })
                    .collect(Collectors.toList());
        }
    }

@@ -2348,4 +2350,39 @@ public class SubscriptionController extends ISub.Stub {
        }
        return false;
    }

    private void notifyOpportunisticSubscriptionInfoChanged() {
        ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
                "telephony.registry"));
        try {
            if (DBG) logd("notifyOpptSubscriptionInfoChanged:");
            tr.notifyOpportunisticSubscriptionInfoChanged();
        } catch (RemoteException ex) {
            // Should never happen because its always available.
        }
    }


    private boolean refreshCachedOpportunisticSubscriptionInfoList() {
        synchronized (mSubInfoListLock) {
            List<SubscriptionInfo> oldOpptCachedList = mCacheOpportunisticSubInfoList;

            mCacheOpportunisticSubInfoList = mCacheActiveSubInfoList.stream()
                    .filter(subscriptionInfo -> subscriptionInfo.isOpportunistic())
                    .collect(Collectors.toList());

            if (DBG_CACHE) {
                if (!mCacheOpportunisticSubInfoList.isEmpty()) {
                    for (SubscriptionInfo si : mCacheOpportunisticSubInfoList) {
                        logd("[refreshCachedOpptSubscriptionInfoList] Setting Cached info="
                                + si);
                    }
                } else {
                    logdl("[refreshCachedOpptSubscriptionInfoList]- no info return");
                }
            }

            return oldOpptCachedList.equals(mCacheOpportunisticSubInfoList);
        }
    }
}