Loading src/java/com/android/internal/telephony/GsmCdmaPhone.java +22 −1 Original line number Diff line number Diff line Loading @@ -3679,6 +3679,7 @@ public class GsmCdmaPhone extends Phone { case EVENT_SUBSCRIPTIONS_CHANGED: logd("EVENT_SUBSCRIPTIONS_CHANGED"); updateUsageSetting(); updateNullCipherNotifier(); break; case EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE: logd("EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE"); Loading Loading @@ -3779,7 +3780,8 @@ public class GsmCdmaPhone extends Phone { && mNullCipherNotifier != null) { ar = (AsyncResult) msg.obj; SecurityAlgorithmUpdate update = (SecurityAlgorithmUpdate) ar.result; mNullCipherNotifier.onSecurityAlgorithmUpdate(mContext, getSubId(), update); mNullCipherNotifier.onSecurityAlgorithmUpdate(mContext, getPhoneId(), getSubId(), update); } break; Loading Loading @@ -5454,6 +5456,25 @@ public class GsmCdmaPhone extends Phone { obtainMessage(EVENT_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED_DONE)); } /** * Update the phoneId -> subId mapping of the null cipher notifier. */ @VisibleForTesting public void updateNullCipherNotifier() { if (!mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) { return; } SubscriptionInfoInternal subInfo = mSubscriptionManagerService .getSubscriptionInfoInternal(getSubId()); boolean active = false; if (subInfo != null) { active = subInfo.isActive(); } mNullCipherNotifier.setSubscriptionMapping(mContext, getPhoneId(), active ? subInfo.getSubscriptionId() : -1); } @Override public boolean isNullCipherAndIntegritySupported() { return mIsNullCipherAndIntegritySupported; Loading src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java +7 −0 Original line number Diff line number Diff line Loading @@ -122,6 +122,13 @@ public class CellularNetworkSecuritySafetySource { updateSafetyCenter(context); } /** * Clears issue state for the identified subscription */ public synchronized void clearNullCipherState(Context context, int subId) { mNullCipherStates.remove(subId); updateSafetyCenter(context); } /** * Enables or disables the identifier disclosure issue and clears any current issues if the * enable state is changed. Loading src/java/com/android/internal/telephony/security/NullCipherNotifier.java +58 −10 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ public class NullCipherNotifier { private final CellularNetworkSecuritySafetySource mSafetySource; private final HashMap<Integer, SubscriptionState> mSubscriptionState = new HashMap<>(); private final HashMap<Integer, Integer> mActiveSubscriptions = new HashMap<>(); private final Object mEnabledLock = new Object(); @GuardedBy("mEnabledLock") Loading Loading @@ -90,15 +91,22 @@ public class NullCipherNotifier { * Adds a security algorithm update. If appropriate, this will trigger a user notification. */ public void onSecurityAlgorithmUpdate( Context context, int subId, SecurityAlgorithmUpdate update) { Context context, int phoneId, int subId, SecurityAlgorithmUpdate update) { Rlog.d(TAG, "Security algorithm update: subId = " + subId + " " + update); if (shouldIgnoreUpdate(update)) { return; } if (!isEnabled()) { Rlog.i(TAG, "Ignoring onSecurityAlgorithmUpdate. Notifier is disabled."); return; } try { mSerializedWorkQueue.execute(() -> { try { maybeUpdateSubscriptionMapping(context, phoneId, subId); SubscriptionState subState = mSubscriptionState.get(subId); if (subState == null) { subState = new SubscriptionState(); Loading @@ -108,12 +116,52 @@ public class NullCipherNotifier { @CellularNetworkSecuritySafetySource.NullCipherState int nullCipherState = subState.update(update); mSafetySource.setNullCipherState(context, subId, nullCipherState); } catch (Throwable t) { Rlog.e(TAG, "Failed to execute onSecurityAlgorithmUpdate " + t.getMessage()); } }); } catch (RejectedExecutionException e) { Rlog.e(TAG, "Failed to schedule onEnableNotifier: " + e.getMessage()); Rlog.e(TAG, "Failed to schedule onSecurityAlgorithmUpdate: " + e.getMessage()); } } /** * Set or update the current phoneId to subId mapping. When a new subId is mapped to a phoneId, * we update the safety source to clear state of the old subId. */ public void setSubscriptionMapping(Context context, int phoneId, int subId) { if (!isEnabled()) { Rlog.i(TAG, "Ignoring setSubscriptionMapping. Notifier is disabled."); } try { mSerializedWorkQueue.execute(() -> { try { maybeUpdateSubscriptionMapping(context, phoneId, subId); } catch (Throwable t) { Rlog.e(TAG, "Failed to update subId mapping. phoneId: " + phoneId + " subId: " + subId + ". " + t.getMessage()); } }); } catch (RejectedExecutionException e) { Rlog.e(TAG, "Failed to schedule setSubscriptionMapping: " + e.getMessage()); } } private void maybeUpdateSubscriptionMapping(Context context, int phoneId, int subId) { final Integer oldSubId = mActiveSubscriptions.put(phoneId, subId); if (oldSubId == null || oldSubId == subId) { return; } // Our subId was updated for this phone, we should clear this subId's state. mSubscriptionState.remove(oldSubId); mSafetySource.clearNullCipherState(context, oldSubId); } /** * Enables null cipher notification; {@code onSecurityAlgorithmUpdate} will start handling * security algorithm updates and send notifications to the user when required. Loading tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java +57 −1 Original line number Diff line number Diff line Loading @@ -3019,7 +3019,63 @@ public class GsmCdmaPhoneTest extends TelephonyTest { processAllMessages(); verify(mNullCipherNotifier, times(1)) .onSecurityAlgorithmUpdate(eq(mContext), eq(0), eq(update)); .onSecurityAlgorithmUpdate(eq(mContext), eq(0), eq(0), eq(update)); } @Test public void testUpdateNullCipherNotifier_flagDisabled() { when(mFeatureFlags.enableModemCipherTransparencyUnsolEvents()).thenReturn(false); Phone phoneUT = makeNewPhoneUT(); phoneUT.sendMessage( mPhoneUT.obtainMessage( Phone.EVENT_SUBSCRIPTIONS_CHANGED, new AsyncResult(null, null, null))); processAllMessages(); verify(mNullCipherNotifier, never()).setSubscriptionMapping(any(), anyInt(), anyInt()); } @Test public void testUpdateNullCipherNotifier_activeSubscription() { when(mFeatureFlags.enableModemCipherTransparencyUnsolEvents()).thenReturn(true); int subId = 10; SubscriptionInfoInternal subInfo = new SubscriptionInfoInternal.Builder().setSimSlotIndex( 0).setId(subId).build(); when(mSubscriptionManagerService.getSubscriptionInfoInternal(subId)).thenReturn( subInfo); doReturn(subId).when(mSubscriptionManagerService) .getSubId(anyInt()); Phone phoneUT = makeNewPhoneUT(); phoneUT.sendMessage( mPhoneUT.obtainMessage( Phone.EVENT_SUBSCRIPTIONS_CHANGED, new AsyncResult(null, null, null))); processAllMessages(); verify(mNullCipherNotifier, times(1)).setSubscriptionMapping(eq(mContext), eq(0), eq(10)); } @Test public void testUpdateNullCipherNotifier_inactiveSubscription() { when(mFeatureFlags.enableModemCipherTransparencyUnsolEvents()).thenReturn(true); int subId = 1; SubscriptionInfoInternal subInfo = new SubscriptionInfoInternal.Builder().setSimSlotIndex( -1).setId(subId).build(); when(mSubscriptionManagerService.getSubscriptionInfoInternal(subId)).thenReturn( subInfo); doReturn(subId).when(mSubscriptionManagerService) .getSubId(anyInt()); Phone phoneUT = makeNewPhoneUT(); phoneUT.sendMessage( mPhoneUT.obtainMessage( Phone.EVENT_SUBSCRIPTIONS_CHANGED, new AsyncResult(null, null, null))); processAllMessages(); verify(mNullCipherNotifier, times(1)).setSubscriptionMapping(eq(mContext), eq(0), eq(-1)); } @Test Loading tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java +20 −0 Original line number Diff line number Diff line Loading @@ -171,6 +171,26 @@ public final class CellularNetworkSecuritySafetySourceTest extends TelephonyTest assertThat(data.getAllValues().get(2).getIssues()).hasSize(2); } @Test public void clearNullCipherState() { ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class); mSafetySource.setNullCipherIssueEnabled(mContext, true); mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_ENCRYPTED); mSafetySource.clearNullCipherState(mContext, 0); // Once for enable, once for encrypted state, and once for clearing state verify(mSafetyCenterManagerWrapper, times(3)).setSafetySourceData(data.capture()); // initial check that our encrypted state update created an issue assertThat(data.getAllValues().get(1).getStatus()).isNotNull(); assertThat(data.getAllValues().get(1).getIssues()).hasSize(1); // assert that our last call to clear state results in no issues sent to SC assertThat(data.getAllValues().get(2).getStatus()).isNotNull(); assertThat(data.getAllValues().get(2).getIssues()).isEmpty(); } @Test public void disableIdentifierDisclosueIssue_nullData() { // We must first enable before disabling, since a standalone call to disable may result in Loading Loading
src/java/com/android/internal/telephony/GsmCdmaPhone.java +22 −1 Original line number Diff line number Diff line Loading @@ -3679,6 +3679,7 @@ public class GsmCdmaPhone extends Phone { case EVENT_SUBSCRIPTIONS_CHANGED: logd("EVENT_SUBSCRIPTIONS_CHANGED"); updateUsageSetting(); updateNullCipherNotifier(); break; case EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE: logd("EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE"); Loading Loading @@ -3779,7 +3780,8 @@ public class GsmCdmaPhone extends Phone { && mNullCipherNotifier != null) { ar = (AsyncResult) msg.obj; SecurityAlgorithmUpdate update = (SecurityAlgorithmUpdate) ar.result; mNullCipherNotifier.onSecurityAlgorithmUpdate(mContext, getSubId(), update); mNullCipherNotifier.onSecurityAlgorithmUpdate(mContext, getPhoneId(), getSubId(), update); } break; Loading Loading @@ -5454,6 +5456,25 @@ public class GsmCdmaPhone extends Phone { obtainMessage(EVENT_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED_DONE)); } /** * Update the phoneId -> subId mapping of the null cipher notifier. */ @VisibleForTesting public void updateNullCipherNotifier() { if (!mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) { return; } SubscriptionInfoInternal subInfo = mSubscriptionManagerService .getSubscriptionInfoInternal(getSubId()); boolean active = false; if (subInfo != null) { active = subInfo.isActive(); } mNullCipherNotifier.setSubscriptionMapping(mContext, getPhoneId(), active ? subInfo.getSubscriptionId() : -1); } @Override public boolean isNullCipherAndIntegritySupported() { return mIsNullCipherAndIntegritySupported; Loading
src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java +7 −0 Original line number Diff line number Diff line Loading @@ -122,6 +122,13 @@ public class CellularNetworkSecuritySafetySource { updateSafetyCenter(context); } /** * Clears issue state for the identified subscription */ public synchronized void clearNullCipherState(Context context, int subId) { mNullCipherStates.remove(subId); updateSafetyCenter(context); } /** * Enables or disables the identifier disclosure issue and clears any current issues if the * enable state is changed. Loading
src/java/com/android/internal/telephony/security/NullCipherNotifier.java +58 −10 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ public class NullCipherNotifier { private final CellularNetworkSecuritySafetySource mSafetySource; private final HashMap<Integer, SubscriptionState> mSubscriptionState = new HashMap<>(); private final HashMap<Integer, Integer> mActiveSubscriptions = new HashMap<>(); private final Object mEnabledLock = new Object(); @GuardedBy("mEnabledLock") Loading Loading @@ -90,15 +91,22 @@ public class NullCipherNotifier { * Adds a security algorithm update. If appropriate, this will trigger a user notification. */ public void onSecurityAlgorithmUpdate( Context context, int subId, SecurityAlgorithmUpdate update) { Context context, int phoneId, int subId, SecurityAlgorithmUpdate update) { Rlog.d(TAG, "Security algorithm update: subId = " + subId + " " + update); if (shouldIgnoreUpdate(update)) { return; } if (!isEnabled()) { Rlog.i(TAG, "Ignoring onSecurityAlgorithmUpdate. Notifier is disabled."); return; } try { mSerializedWorkQueue.execute(() -> { try { maybeUpdateSubscriptionMapping(context, phoneId, subId); SubscriptionState subState = mSubscriptionState.get(subId); if (subState == null) { subState = new SubscriptionState(); Loading @@ -108,12 +116,52 @@ public class NullCipherNotifier { @CellularNetworkSecuritySafetySource.NullCipherState int nullCipherState = subState.update(update); mSafetySource.setNullCipherState(context, subId, nullCipherState); } catch (Throwable t) { Rlog.e(TAG, "Failed to execute onSecurityAlgorithmUpdate " + t.getMessage()); } }); } catch (RejectedExecutionException e) { Rlog.e(TAG, "Failed to schedule onEnableNotifier: " + e.getMessage()); Rlog.e(TAG, "Failed to schedule onSecurityAlgorithmUpdate: " + e.getMessage()); } } /** * Set or update the current phoneId to subId mapping. When a new subId is mapped to a phoneId, * we update the safety source to clear state of the old subId. */ public void setSubscriptionMapping(Context context, int phoneId, int subId) { if (!isEnabled()) { Rlog.i(TAG, "Ignoring setSubscriptionMapping. Notifier is disabled."); } try { mSerializedWorkQueue.execute(() -> { try { maybeUpdateSubscriptionMapping(context, phoneId, subId); } catch (Throwable t) { Rlog.e(TAG, "Failed to update subId mapping. phoneId: " + phoneId + " subId: " + subId + ". " + t.getMessage()); } }); } catch (RejectedExecutionException e) { Rlog.e(TAG, "Failed to schedule setSubscriptionMapping: " + e.getMessage()); } } private void maybeUpdateSubscriptionMapping(Context context, int phoneId, int subId) { final Integer oldSubId = mActiveSubscriptions.put(phoneId, subId); if (oldSubId == null || oldSubId == subId) { return; } // Our subId was updated for this phone, we should clear this subId's state. mSubscriptionState.remove(oldSubId); mSafetySource.clearNullCipherState(context, oldSubId); } /** * Enables null cipher notification; {@code onSecurityAlgorithmUpdate} will start handling * security algorithm updates and send notifications to the user when required. Loading
tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java +57 −1 Original line number Diff line number Diff line Loading @@ -3019,7 +3019,63 @@ public class GsmCdmaPhoneTest extends TelephonyTest { processAllMessages(); verify(mNullCipherNotifier, times(1)) .onSecurityAlgorithmUpdate(eq(mContext), eq(0), eq(update)); .onSecurityAlgorithmUpdate(eq(mContext), eq(0), eq(0), eq(update)); } @Test public void testUpdateNullCipherNotifier_flagDisabled() { when(mFeatureFlags.enableModemCipherTransparencyUnsolEvents()).thenReturn(false); Phone phoneUT = makeNewPhoneUT(); phoneUT.sendMessage( mPhoneUT.obtainMessage( Phone.EVENT_SUBSCRIPTIONS_CHANGED, new AsyncResult(null, null, null))); processAllMessages(); verify(mNullCipherNotifier, never()).setSubscriptionMapping(any(), anyInt(), anyInt()); } @Test public void testUpdateNullCipherNotifier_activeSubscription() { when(mFeatureFlags.enableModemCipherTransparencyUnsolEvents()).thenReturn(true); int subId = 10; SubscriptionInfoInternal subInfo = new SubscriptionInfoInternal.Builder().setSimSlotIndex( 0).setId(subId).build(); when(mSubscriptionManagerService.getSubscriptionInfoInternal(subId)).thenReturn( subInfo); doReturn(subId).when(mSubscriptionManagerService) .getSubId(anyInt()); Phone phoneUT = makeNewPhoneUT(); phoneUT.sendMessage( mPhoneUT.obtainMessage( Phone.EVENT_SUBSCRIPTIONS_CHANGED, new AsyncResult(null, null, null))); processAllMessages(); verify(mNullCipherNotifier, times(1)).setSubscriptionMapping(eq(mContext), eq(0), eq(10)); } @Test public void testUpdateNullCipherNotifier_inactiveSubscription() { when(mFeatureFlags.enableModemCipherTransparencyUnsolEvents()).thenReturn(true); int subId = 1; SubscriptionInfoInternal subInfo = new SubscriptionInfoInternal.Builder().setSimSlotIndex( -1).setId(subId).build(); when(mSubscriptionManagerService.getSubscriptionInfoInternal(subId)).thenReturn( subInfo); doReturn(subId).when(mSubscriptionManagerService) .getSubId(anyInt()); Phone phoneUT = makeNewPhoneUT(); phoneUT.sendMessage( mPhoneUT.obtainMessage( Phone.EVENT_SUBSCRIPTIONS_CHANGED, new AsyncResult(null, null, null))); processAllMessages(); verify(mNullCipherNotifier, times(1)).setSubscriptionMapping(eq(mContext), eq(0), eq(-1)); } @Test Loading
tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java +20 −0 Original line number Diff line number Diff line Loading @@ -171,6 +171,26 @@ public final class CellularNetworkSecuritySafetySourceTest extends TelephonyTest assertThat(data.getAllValues().get(2).getIssues()).hasSize(2); } @Test public void clearNullCipherState() { ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class); mSafetySource.setNullCipherIssueEnabled(mContext, true); mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_ENCRYPTED); mSafetySource.clearNullCipherState(mContext, 0); // Once for enable, once for encrypted state, and once for clearing state verify(mSafetyCenterManagerWrapper, times(3)).setSafetySourceData(data.capture()); // initial check that our encrypted state update created an issue assertThat(data.getAllValues().get(1).getStatus()).isNotNull(); assertThat(data.getAllValues().get(1).getIssues()).hasSize(1); // assert that our last call to clear state results in no issues sent to SC assertThat(data.getAllValues().get(2).getStatus()).isNotNull(); assertThat(data.getAllValues().get(2).getIssues()).isEmpty(); } @Test public void disableIdentifierDisclosueIssue_nullData() { // We must first enable before disabling, since a standalone call to disable may result in Loading