Skip to content

Commit

Permalink
fix(telephony): prevent SIM subscriptions sync when altering SIM state
Browse files Browse the repository at this point in the history
To reproduce this:
1. Ensure the device is using the legacy radio interface layer (RIL) to
   control SIM cards
2. Toggle the same SIM card multiple times in a row
3. Observe the SIM card forcefully returning to its original state due
   to the synchronization with its schedules

Deep-dive explanation:
When performing the SIM subscription state mutation for the same SIM
card multiple times in a row (see lines 1-12 & 13-24), this will sooner
trigger in parallel the global carrier config changed event listener
(see line 25), which in turn will run the SIM subscriptions sync
process. In this case, the SIM subscriptions sync process may
short-circuit and terminate fast, or it may force override user's
preference applied a moment ago through the scheduler (see line 41).
This is because the last{Activated/Deactivated}Time fields used to
determine whether to keep the user's preference within the allowed
period or not was reset during the SIM subscriptions sync process, since
the SIM subscription can be temporarily unavailable (see line 38) during
state transition.

The fix:
This patch fix introduces an atomic flag to block the synchronization of the
internal state of all SIM subscriptions during SIM subscription state
mutations.
Currently, this issue affects heavily only devices using legacy RIL, but
we'll use the flag on the newer RIL too, since there's a call to the
database that may take longer to complete in some circumstances.

1 D  7SIM.SimListViewModel handleOnSimEnabledStateChanged(simEntryId=1,enabled=false).
2 D  7SIM.TelephonyController setSimState(slotIndex=1,enabled=false,keepDisabledAcrossBoots=true) : In sync block.
3 V  7SIM.SubscriptionsImplLegacy persistSubscription(sub=Subscription { id=1 slotIndex=1 simState=DISABLED iconTint=-4056997 name=Vodafone lastActivatedTime=-999999999-01-01T00:00 lastDeactivatedTime=2024-08-23T14:51 keepDisabledAcrossBoots=true }).
4 D  7SIM.SubscriptionsImplLegacy persistSubscriptionState(subId=1,state=DISABLED).
5 V  7SIM.SubscriptionsImplLegacy addOnSimStatusChangedListener().
6 V  7SIM.SubscriptionsImplLegacy registerCarrierConfigChangedReceiver().
7 V  7SIM.SubscriptionsImplLegacy onReceive() : intent=Intent { act=android.telephony.action.SIM_CARD_STATE_CHANGED flg=0x5000010 (has extras) }
8 V  7SIM.SubscriptionsImplLegacy dispatchOnSimStatusChanged(slotIndex=1,state=1).
9 V  7SIM.TelephonyController onSimStatusChanged(slotIndex=1,state=1).
10 V  7SIM.SubscriptionsImplLegacy removeOnSimStatusChangedListener().
11 V  7SIM.SubscriptionsImplLegacy unregisterCarrierConfigChangedReceiver().
12 D  7SIM.TelephonyController handleOnSetSimPowerStateForSlotFinished(resCode=1) : requestMetadata=Bundle[{last_activated_time=-999999999-01-01T00:00, last_deactivated_time=2024-08-23T14:51, subscription=Subscription { id=1 slotIndex=1 simState=DISABLED iconTint=-4056997 name=Vodafone lastActivatedTime=-999999999-01-01T00:00 lastDeactivatedTime=2024-08-23T14:51 keepDisabledAcrossBoots=true }, keep_disabled_across_boots=true}], requestFailed=false,shouldNotifyAllListeners=false

13 D  7SIM.SimListViewModel handleOnSimEnabledStateChanged(simEntryId=1,enabled=true).
14 D  7SIM.TelephonyController setSimState(slotIndex=1,enabled=true,keepDisabledAcrossBoots=false) : In sync block.
15 V  7SIM.SubscriptionsImplLegacy persistSubscription(sub=Subscription { id=1 slotIndex=1 simState=ENABLED iconTint=-4056997 name=Vodafone lastActivatedTime=2024-08-23T14:51 lastDeactivatedTime=-999999999-01-01T00:00 keepDisabledAcrossBoots=false }).
16 D  7SIM.SubscriptionsImplLegacy persistSubscriptionState(subId=1,state=ENABLED).
17 V  7SIM.SubscriptionsImplLegacy addOnSimStatusChangedListener().
18 V  7SIM.SubscriptionsImplLegacy registerCarrierConfigChangedReceiver().
19 V  7SIM.SubscriptionsImplLegacy onReceive() : intent=Intent { act=android.telephony.action.SIM_CARD_STATE_CHANGED flg=0x5000010 (has extras) }
20 V  7SIM.SubscriptionsImplLegacy dispatchOnSimStatusChanged(slotIndex=1,state=11).
21 V  7SIM.TelephonyController onSimStatusChanged(slotIndex=1,state=11).
22 V  7SIM.SubscriptionsImplLegacy removeOnSimStatusChangedListener().
23 V  7SIM.SubscriptionsImplLegacy unregisterCarrierConfigChangedReceiver().
24 D  7SIM.TelephonyController handleOnSetSimPowerStateForSlotFinished(resCode=11) : requestMetadata=Bundle[{last_activated_time=2024-08-23T14:51, last_deactivated_time=-999999999-01-01T00:00, subscription=Subscription { id=1 slotIndex=1 simState=ENABLED iconTint=-4056997 name=Vodafone lastActivatedTime=2024-08-23T14:51 lastDeactivatedTime=-999999999-01-01T00:00 keepDisabledAcrossBoots=false }, keep_disabled_across_boots=false}], requestFailed=false,shouldNotifyAllListeners=true

25 D  7SIM.DirectBootAwareBroadcastReceiver onReceive() : intent=Intent { act=android.telephony.action.CARRIER_CONFIG_CHANGED flg=0x15000010 cmp=com.github.iusmac.sevensim/.DirectBootAwareBroadcastReceiver (has extras) }
26 D  7SIM.ForegroundService onCreate().
27 D  7SIM.ForegroundService onStartCommand(intent=Intent { act=ACTION_SUBSCRIPTIONS_CHANGED cmp=com.github.iusmac.sevensim/.ForegroundService (has extras) },flags=0,startId=1).
28 D  7SIM.ForegroundService Worker.execute(taskId=1) Add : mQueueSize=0.
29 D  7SIM.ForegroundService onStartCommand(intent=Intent { act=ACTION_SYNC_SUBSCRIPTION_ENABLED_STATE cmp=com.github.iusmac.sevensim/.ForegroundService (has extras) },flags=0,startId=2).
30 D  7SIM.ForegroundService Worker.execute(taskId=1) Start : mQueueSize=1.
31 D  7SIM.ForegroundService Worker.execute(taskId=2) Add : mQueueSize=1.
32 D  7SIM.ForegroundService onStartCommand(intent=Intent { act=ACTION_UPDATE_NEXT_WEEKLY_REPEAT_SCHEDULE_PROCESSING_ITER cmp=com.github.iusmac.sevensim/.ForegroundService (has extras) },flags=0,startId=3).
33 D  7SIM.ForegroundService Worker.execute(taskId=3) Add : mQueueSize=2.
34 D  7SIM.SubscriptionsImplLegacy syncSubscriptions(dateTime=2024-08-23T14:51:08.280) : Subscription { id=2 slotIndex=0 simState=ENABLED iconTint=-13408298 name=Vodafone (work) lastActivatedTime=-999999999-01-01T00:00 lastDeactivatedTime=-999999999-01-01T00:00 keepDisabledAcrossBoots=false },currentSubState=ENABLED,expectedSubState=UNKNOWN,existsInUsableList=false.
35 D  7SIM.SubscriptionsImplLegacy persistSubscriptionState(subId=2,state=ENABLED).
36 D  7SIM.SubscriptionsImplLegacy syncSubscriptions(dateTime=2024-08-23T14:51:08.280) : Subscription { id=1 slotIndex=-1 simState=UNKNOWN iconTint=-16777216 name= lastActivatedTime=2024-08-23T14:51 lastDeactivatedTime=-999999999-01-01T00:00 keepDisabledAcrossBoots=false }.
37 V  7SIM.SubscriptionsImplLegacy persistSubscription(sub=Subscription { id=1 slotIndex=-1 simState=UNKNOWN iconTint=-16777216 name= lastActivatedTime=-999999999-01-01T00:00 lastDeactivatedTime=-999999999-01-01T00:00 keepDisabledAcrossBoots=false }).
38 D  7SIM.SubscriptionsImplLegacy persistSubscriptionState(subId=1,state=UNKNOWN).
39 D  7SIM.ForegroundService Worker.execute(taskId=1) Finish : mQueueSize=2.
40 D  7SIM.ForegroundService Worker.execute(taskId=2) Start : mQueueSize=2.
41 D  7SIM.SubscriptionScheduler syncSubscriptionEnabledState(subId=2,compareTime=2024-08-23T14:51:08.280,overrideUserPreference=false) : Subscription { id=2 slotIndex=0 simState=ENABLED iconTint=-13408298 name=Vodafone (work) lastActivatedTime=-999999999-01-01T00:00 lastDeactivatedTime=-999999999-01-01T00:00 keepDisabledAcrossBoots=false },nearestEnableTime=Optional.empty,nearestDisableTime=Optional[2024-08-19T08:00],expectedEnabled=false,isInCall=false.
42 D  7SIM.TelephonyController setSimState(slotIndex=0,enabled=false,keepDisabledAcrossBoots=false) : In sync block.

Signed-off-by: iusmac <iusico.maxim@libero.it>
  • Loading branch information
iusmac committed Aug 23, 2024
1 parent 34d6f20 commit f84351e
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ public void setUiccApplicationsEnabled(final int subId, final boolean enabled) {
return;
}

// Ensure SIM subscription syncing cannot start in parallel during this operation
mSubscriptions.mBlockSubscriptionsSyncFlag.set(true);

sub.setSimState(TelephonyUtils.simStateInt(enabled));
sub.setLastActivatedTime(enabled ? LocalDateTime.now(ZoneId.systemDefault()) :
LocalDateTime.MIN);
Expand All @@ -79,5 +82,7 @@ public void setUiccApplicationsEnabled(final int subId, final boolean enabled) {
mSubscriptions.persistSubscription(sub);

mSubManager.setUiccApplicationsEnabled(subId, enabled);

mSubscriptions.mBlockSubscriptionsSyncFlag.set(false);
}
}
11 changes: 11 additions & 0 deletions src/com/github/iusmac/sevensim/telephony/Subscriptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ public void onReceive(final Context context, final Intent intent) {
*/
private final AtomicBoolean mCarrierConfigChangedReceiverRegistered = new AtomicBoolean();

/**
* Atomic flag indicating whether the synchronization of the internal state of all SIM
* subscriptions is blocked or not. Use this to ensure atomic SIM subscription state mutations.
*/
final AtomicBoolean mBlockSubscriptionsSyncFlag = new AtomicBoolean();

private final Context mContext;
protected final Logger mLogger;
protected final SubscriptionManager mSubscriptionManager;
Expand Down Expand Up @@ -295,6 +301,11 @@ public void removeOnSubscriptionsChangedListener(
*/
@WorkerThread
public void syncSubscriptions(final LocalDateTime dateTime) {
if (mBlockSubscriptionsSyncFlag.get()) {
mLogger.w("syncSubscriptions(dateTime=%s) : Operation has been disallowed.", dateTime);
return;
}

final List<String> removedSubIds = new ArrayList<>(getPersistedUsableSubIds());
final List<String> usableSubIds = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ public void setSimState(final int slotIndex, final boolean enabled,
return;
}

// Ensure SIM subscription syncing cannot start in parallel during this operation
mSubscriptions.mBlockSubscriptionsSyncFlag.set(true);

// Keep track of SIM state whenever it's mutated. This will be persisted in a volatile
// memory, so that we can further restore all relevant data. This because when powering
// down the SIM is the same as removing it, which means the SIM will completely
Expand Down Expand Up @@ -226,6 +229,8 @@ public void setSimState(final int slotIndex, final boolean enabled,
}
handleOnSetSimPowerStateForSlotFinished(resCode);
}

mSubscriptions.mBlockSubscriptionsSyncFlag.set(false);
}
}

Expand Down

0 comments on commit f84351e

Please sign in to comment.