Loading src/java/com/android/internal/telephony/satellite/SatelliteController.java +149 −35 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ import android.annotation.ArrayRes; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.AlertDialog; import android.app.Notification; import android.app.NotificationChannel; Loading Loading @@ -117,6 +118,7 @@ import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; import android.provider.Telephony; import android.telecom.TelecomManager; Loading Loading @@ -250,8 +252,8 @@ public class SatelliteController extends Handler { public static final long DEFAULT_CARRIER_EMERGENCY_CALL_WAIT_FOR_CONNECTION_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(30); /** Sets report entitled metrics cool down to 23 hours to help enforcing privacy requirement.*/ private static final long WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS = /** Sets general metrics report cool down to 23 hours to help enforcing privacy requirement.*/ private static final long REGULAR_METRIC_REPORTING_INTERVAL_MILLIS = TimeUnit.HOURS.toMillis(23); /** Loading Loading @@ -311,7 +313,7 @@ public class SatelliteController extends Handler { private static final int EVENT_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_DONE = 51; protected static final int EVENT_WAIT_FOR_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_RESPONSE_TIMED_OUT = 52; private static final int EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT = 53; private static final int EVENT_WAIT_FOR_REGULAR_METRICS_REPORT_HYSTERESIS_TIMED_OUT = 53; protected static final int EVENT_SATELLITE_REGISTRATION_FAILURE = 54; private static final int EVENT_TERRESTRIAL_NETWORK_AVAILABLE_CHANGED = 55; private static final int EVENT_SET_NETWORK_SELECTION_AUTO_DONE = 56; Loading Loading @@ -777,6 +779,17 @@ public class SatelliteController extends Handler { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) protected boolean mSatelliteAccessAllowed = false; @Nullable private AlarmManager mAlarmManager; @NonNull private final AlarmManager.OnAlarmListener mRegularMetricReportAlarmListener = () -> { plogd("onAlarm: regular metric report hysteresis timer expired"); Message msg = SatelliteController.getInstance().obtainMessage( EVENT_WAIT_FOR_REGULAR_METRICS_REPORT_HYSTERESIS_TIMED_OUT, null); msg.sendToTarget(); }; public static final int RESULT_RECEIVER_COUNT_ANOMALY_THRESHOLD = 500; protected final Object mResultReceiverTotalCountLock = new Object(); @GuardedBy("mResultReceiverTotalCountLock") Loading Loading @@ -1031,6 +1044,8 @@ public class SatelliteController extends Handler { sendRequestAsync(CMD_GET_SATELLITE_ENABLED_FOR_CARRIER, null, phoneToSendRequest); } mAlarmManager = mContext.getSystemService(AlarmManager.class); scheduleRegularMetricReportTimer(); logd("Satellite Tracker is created"); } Loading Loading @@ -2145,18 +2160,9 @@ public class SatelliteController extends Handler { break; } case EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT: { // TODO: b/366329504 report carrier roaming metrics for multiple subscription IDs. synchronized (mSupportedSatelliteServicesLock) { int defaultSubId = mSubscriptionManagerService.getDefaultSubId(); boolean isEntitled = mSatelliteEntitlementStatusPerCarrier.get(defaultSubId, false); mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(defaultSubId, isEntitled); } sendMessageDelayed(obtainMessage( EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT), WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS); case EVENT_WAIT_FOR_REGULAR_METRICS_REPORT_HYSTERESIS_TIMED_OUT: { handleEntireEntitlementMetricReport(); handleEntireProvisionMetricReport(); break; } Loading Loading @@ -4922,15 +4928,7 @@ public class SatelliteController extends Handler { synchronized (mSupportedSatelliteServicesLock) { if (mSatelliteEntitlementStatusPerCarrier.get(subId, false) != entitlementEnabled) { logd("update the carrier satellite enabled to " + entitlementEnabled); mSatelliteEntitlementStatusPerCarrier.put(subId, entitlementEnabled); mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(subId, entitlementEnabled); if (hasMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT)) { removeMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT); sendMessageDelayed(obtainMessage( EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT), WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS); } handleIndividualEntitlementMetricReport(subId, entitlementEnabled); try { mSubscriptionManagerService.setSubscriptionProperty(subId, SATELLITE_ENTITLEMENT_STATUS, entitlementEnabled ? "1" : "0"); Loading Loading @@ -5424,9 +5422,7 @@ public class SatelliteController extends Handler { updateCachedDeviceProvisionStatus(); // Report updated provisioned status to metrics. synchronized (mSatelliteTokenProvisionedLock) { boolean isProvisioned = !mProvisionedSubscriberId.isEmpty() && mProvisionedSubscriberId.containsValue(Boolean.TRUE); mControllerMetricsStats.setIsProvisioned(isProvisioned); handleEntireProvisionMetricReport(); } selectBindingSatelliteSubscription(false); evaluateCarrierRoamingNtnEligibilityChange(); Loading Loading @@ -6566,14 +6562,7 @@ public class SatelliteController extends Handler { entitlementStatus = "0"; } boolean result = entitlementStatus.equals("1"); mSatelliteEntitlementStatusPerCarrier.put(subId, result); mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(subId, result); if (hasMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT)) { removeMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT); sendMessageDelayed(obtainMessage( EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT), WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS); } handleIndividualEntitlementMetricReport(subId, result); } if (!mSatelliteEntitlementStatusPerCarrier.get(subId, false)) { Loading Loading @@ -9724,6 +9713,131 @@ public class SatelliteController extends Handler { return mWifiStateEnabled.get(); } @GuardedBy("mSupportedSatelliteServicesLock") private void handleEntireEntitlementMetricReport() { synchronized (mSupportedSatelliteServicesLock) { int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(true); if (activeSubIds != null && activeSubIds.length > 0) { for (int subId : activeSubIds) { boolean isSubIdEntitled = mSatelliteEntitlementStatusPerCarrier.get(subId); mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(subId, isSubIdEntitled); plogd("handleEntitlementMetricReport: subId=" + subId + ", isSubEntitled=" + isSubIdEntitled); } } else { loge("handleEntireEntitlementMetricReport: no active subId"); } } scheduleRegularMetricReportTimer(); } @GuardedBy("mSupportedSatelliteServicesLock") private void handleIndividualEntitlementMetricReport(int subId, boolean isSubscriptionEntitled) { synchronized (mSupportedSatelliteServicesLock) { mSatelliteEntitlementStatusPerCarrier.put(subId, isSubscriptionEntitled); mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(subId, isSubscriptionEntitled); } } @GuardedBy("mSatelliteTokenProvisionedLock") private void handleEntireProvisionMetricReport() { logd("handleEntireProvisionMetricReport:"); // Hold the final aggregated status for each carrierId. Map<Integer, CarrierReportInfo> reportDataPerCarrier = new HashMap<>(); synchronized (mSatelliteTokenProvisionedLock) { // Aggregate provision status and isNtnOnlyCarrier info per carrierId List<SubscriptionInfo> allSubInfos = mSubscriptionManagerService.getAllSubInfoList( mContext.getOpPackageName(), mContext.getAttributionTag()); for (SubscriptionInfo info : allSubInfos) { int subId = info.getSubscriptionId(); boolean isNtnOnlySubId = info.isOnlyNonTerrestrialNetwork(); boolean isActiveSubId = info.isActive(); if (!isNtnOnlySubId && !isActiveSubId) { plogd("handleEntireProvisionMetricReport: subId=" + subId + " is neither NTN-only nor active. Skipping."); continue; } int carrierId = SatelliteServiceUtils.getCarrierIdFromSubscription(subId); if (carrierId == TelephonyManager.UNKNOWN_CARRIER_ID && !isNtnOnlySubId) { plogd("handleEntireProvisionMetricReport: neither valid carrierId " + "nor NTN-only, subId=" + subId + ". Skipping."); continue; } String subscriberId = getSubscriberIdAndType(info).first; boolean isProvisioned = mProvisionedSubscriberId.getOrDefault(subscriberId, false); CarrierReportInfo carrierInfo = reportDataPerCarrier.computeIfAbsent( carrierId, key -> new CarrierReportInfo()); carrierInfo.aggregate(isProvisioned, isNtnOnlySubId); } } // Report the aggregated status for each carrierId if (reportDataPerCarrier.isEmpty()) { plogd("handleEntireProvisionMetricReport: No reportable data found"); } else { plogd("handleEntireProvisionMetricReport: Reporting final aggregated status for " + reportDataPerCarrier.size() + " carrier(s)."); for (Map.Entry<Integer, CarrierReportInfo> reportEntry : reportDataPerCarrier.entrySet()) { int carrierId = reportEntry.getKey(); CarrierReportInfo info = reportEntry.getValue(); plogd("handleEntireProvisionMetricReport: Final report for carrierId=" + carrierId + ", isProvisioned=" + info.mIsAnySubProvisioned + ", isNtnOnlyCarrier=" + info.mIsNtnOnlyCarrier); mControllerMetricsStats.setIsProvisioned( carrierId, info.mIsAnySubProvisioned, info.mIsNtnOnlyCarrier); } } scheduleRegularMetricReportTimer(); } // Helper class to store aggregated information per carrierId. private static class CarrierReportInfo { boolean mIsAnySubProvisioned = false; boolean mIsNtnOnlyCarrier = false; void aggregate(boolean isProvisioned, boolean isNtnOnlyCarrier) { // if any subId for the carrier was provisioned, this carrier is provisioned if (isProvisioned) { this.mIsAnySubProvisioned = true; } this.mIsNtnOnlyCarrier = isNtnOnlyCarrier; } } private void scheduleRegularMetricReportTimer() { if (mAlarmManager == null) { plogd("scheduleRegularMetricReportTimer: AlarmManager is null"); return; } mAlarmManager.cancel(mRegularMetricReportAlarmListener); mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, getElapsedRealtime() + REGULAR_METRIC_REPORTING_INTERVAL_MILLIS, TAG, new HandlerExecutor(this), new WorkSource(), mRegularMetricReportAlarmListener); } /** * Uses this function to set AlarmManager object for testing. * * @param alarmManager The instance of AlarmManager. */ @VisibleForTesting public void setAlarmManager(AlarmManager alarmManager) { mAlarmManager = alarmManager; } /** * Method to return the current data plan for the registered plmn based on entitlement * provisioning information. Note: If no information at Loading src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java +8 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.telephony.PersistentLogger; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.satellite.AntennaPosition; import android.telephony.satellite.EarfcnRange; import android.telephony.satellite.NtnSignalStrength; Loading Loading @@ -738,6 +739,13 @@ public class SatelliteServiceUtils { return satelliteController.isInCarrierRoamingNbIotNtn(phone); } /** Returns the carrier ID of the given subscription id. */ public static int getCarrierIdFromSubscription(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); Phone phone = PhoneFactory.getPhone(phoneId); return phone != null ? phone.getCarrierId() : TelephonyManager.UNKNOWN_CARRIER_ID; } private static void logd(@NonNull String log) { Log.d(TAG, log); } Loading src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java +4 −3 Original line number Diff line number Diff line Loading @@ -390,12 +390,13 @@ public class ControllerMetricsStats { } /** Capture the latest provisioned state for satellite service */ @VisibleForTesting public void setIsProvisioned(boolean isProvisioned) { logd("setIsProvisioned:" + isProvisioned); public void setIsProvisioned(int carrierId, boolean isProvisioned, boolean isNtnOnlyCarrier) { logd("setIsProvisioned: carrierId=" + carrierId + ", isProvisioned=" + isProvisioned); mSatelliteStats.onSatelliteControllerMetrics( new SatelliteStats.SatelliteControllerParams.Builder() .setCarrierId(carrierId) .setIsProvisioned(isProvisioned) .setIsNtnOnlyCarrier(isNtnOnlyCarrier) .build()); } Loading tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java +67 −1 Original line number Diff line number Diff line Loading @@ -108,6 +108,7 @@ import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; Loading @@ -122,6 +123,7 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.NotificationManager; import android.app.usage.NetworkStatsManager; import android.content.BroadcastReceiver; Loading @@ -146,6 +148,7 @@ import android.os.OutcomeReceiver; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.WorkSource; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; Loading Loading @@ -209,6 +212,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; Loading Loading @@ -290,6 +294,10 @@ public class SatelliteControllerTest extends TelephonyTest { @Mock private SubscriptionInfo mSubscriptionInfo; @Mock private PackageManager mMockPManager; @Mock private Intent mMockLocationIntent; @Mock private AlarmManager mMockAlarmManager; @Captor private ArgumentCaptor<AlarmManager.OnAlarmListener> mAlarmListenerCaptor; private Semaphore mIIntegerConsumerSemaphore = new Semaphore(0); private IIntegerConsumer mIIntegerConsumer = new IIntegerConsumer.Stub() { Loading Loading @@ -736,6 +744,10 @@ public class SatelliteControllerTest extends TelephonyTest { doReturn(true).when(mFeatureFlags).satelliteImproveMultiThreadDesign(); doReturn(TEST_ALL_SATELLITE_PLMN_SET).when(mMockSatelliteController).getAllPlmnSet(); mSatelliteControllerUT.setAlarmManager(mMockAlarmManager); doNothing().when(mMockAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class)); doNothing().when(mMockAlarmManager).setExact(anyInt(), anyLong(), anyString(), any(Executor.class), any(WorkSource.class), mAlarmListenerCaptor.capture()); } @After Loading Loading @@ -1763,7 +1775,7 @@ public class SatelliteControllerTest extends TelephonyTest { } @Test public void testRegisterForSatelliteProvisionStateChanged() { public void testRegisterForSatelliteProvisionStateChanged() throws Exception { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); Semaphore semaphore = new Semaphore(0); ISatelliteProvisionStateCallback callback = Loading Loading @@ -4446,9 +4458,63 @@ public class SatelliteControllerTest extends TelephonyTest { @Test public void testProvisionSatellite() throws Exception { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); when(mFeatureFlags.satelliteImproveMultiThreadDesign()).thenReturn(true); verifyRequestSatelliteSubscriberProvisionStatus(); List<SatelliteSubscriberInfo> inputList = getExpectedSatelliteSubscriberInfoList(); try { replaceInstance(SatelliteController.class, "sInstance", null, mSatelliteControllerUT); } catch (Exception ex) { loge(ex.toString()); } reset(mMockControllerMetricsStats); reset(mMockAlarmManager); doNothing().when(mMockAlarmManager).setExact(anyInt(), anyLong(), anyString(), any(Executor.class), any(WorkSource.class), mAlarmListenerCaptor.capture()); verifyProvisionSatellite(inputList); int numberOfCarriers = (int) inputList.stream() .map(SatelliteSubscriberInfo::getCarrierId) .distinct() .count(); // TODO b/409584433 for now handleRequestProvisionSatellite is invoked 2 times. // expectedMetricReportCallCount should be restore to numberOfCarriers eventually. int expectedMetricReportCallCount = numberOfCarriers; if (mFeatureFlags.satelliteImproveMultiThreadDesign()) { expectedMetricReportCallCount *= 2; } verify(mMockControllerMetricsStats, times(expectedMetricReportCallCount)).setIsProvisioned( anyInt(), eq(true), anyBoolean()); verify(mMockAlarmManager, atLeastOnce()).cancel(any(AlarmManager.OnAlarmListener.class)); verify(mMockAlarmManager, atLeastOnce()).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(), any(Executor.class), any(WorkSource.class), any(AlarmManager.OnAlarmListener.class)); AlarmManager.OnAlarmListener capturedListener = mAlarmListenerCaptor.getValue(); if (capturedListener == null) { fail("AlarmListener was not captured by AlarmManager.setExact()"); } final CountDownLatch countDownLatch = new CountDownLatch(numberOfCarriers); doAnswer(invocation -> { countDownLatch.countDown(); return null; }).when(mMockControllerMetricsStats).setIsProvisioned(anyInt(), anyBoolean(), anyBoolean()); capturedListener.onAlarm(); processAllMessages(); try { if (!countDownLatch.await(2, TimeUnit.SECONDS)) { fail("Handler did not process the expected message (latch timed out)"); } } catch (InterruptedException ex) { loge(ex.toString()); } expectedMetricReportCallCount += numberOfCarriers; verify(mMockControllerMetricsStats, times(expectedMetricReportCallCount)).setIsProvisioned( anyInt(), eq(true), anyBoolean()); verify(mMockAlarmManager, atLeast(2)).cancel(any(AlarmManager.OnAlarmListener.class)); verify(mMockAlarmManager, atLeast(2)).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(), any(Executor.class), any(WorkSource.class), any(AlarmManager.OnAlarmListener.class)); } private void verifyProvisionSatellite(List<SatelliteSubscriberInfo> inputList) { Loading Loading
src/java/com/android/internal/telephony/satellite/SatelliteController.java +149 −35 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ import android.annotation.ArrayRes; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.AlertDialog; import android.app.Notification; import android.app.NotificationChannel; Loading Loading @@ -117,6 +118,7 @@ import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; import android.provider.Telephony; import android.telecom.TelecomManager; Loading Loading @@ -250,8 +252,8 @@ public class SatelliteController extends Handler { public static final long DEFAULT_CARRIER_EMERGENCY_CALL_WAIT_FOR_CONNECTION_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(30); /** Sets report entitled metrics cool down to 23 hours to help enforcing privacy requirement.*/ private static final long WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS = /** Sets general metrics report cool down to 23 hours to help enforcing privacy requirement.*/ private static final long REGULAR_METRIC_REPORTING_INTERVAL_MILLIS = TimeUnit.HOURS.toMillis(23); /** Loading Loading @@ -311,7 +313,7 @@ public class SatelliteController extends Handler { private static final int EVENT_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_DONE = 51; protected static final int EVENT_WAIT_FOR_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_RESPONSE_TIMED_OUT = 52; private static final int EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT = 53; private static final int EVENT_WAIT_FOR_REGULAR_METRICS_REPORT_HYSTERESIS_TIMED_OUT = 53; protected static final int EVENT_SATELLITE_REGISTRATION_FAILURE = 54; private static final int EVENT_TERRESTRIAL_NETWORK_AVAILABLE_CHANGED = 55; private static final int EVENT_SET_NETWORK_SELECTION_AUTO_DONE = 56; Loading Loading @@ -777,6 +779,17 @@ public class SatelliteController extends Handler { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) protected boolean mSatelliteAccessAllowed = false; @Nullable private AlarmManager mAlarmManager; @NonNull private final AlarmManager.OnAlarmListener mRegularMetricReportAlarmListener = () -> { plogd("onAlarm: regular metric report hysteresis timer expired"); Message msg = SatelliteController.getInstance().obtainMessage( EVENT_WAIT_FOR_REGULAR_METRICS_REPORT_HYSTERESIS_TIMED_OUT, null); msg.sendToTarget(); }; public static final int RESULT_RECEIVER_COUNT_ANOMALY_THRESHOLD = 500; protected final Object mResultReceiverTotalCountLock = new Object(); @GuardedBy("mResultReceiverTotalCountLock") Loading Loading @@ -1031,6 +1044,8 @@ public class SatelliteController extends Handler { sendRequestAsync(CMD_GET_SATELLITE_ENABLED_FOR_CARRIER, null, phoneToSendRequest); } mAlarmManager = mContext.getSystemService(AlarmManager.class); scheduleRegularMetricReportTimer(); logd("Satellite Tracker is created"); } Loading Loading @@ -2145,18 +2160,9 @@ public class SatelliteController extends Handler { break; } case EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT: { // TODO: b/366329504 report carrier roaming metrics for multiple subscription IDs. synchronized (mSupportedSatelliteServicesLock) { int defaultSubId = mSubscriptionManagerService.getDefaultSubId(); boolean isEntitled = mSatelliteEntitlementStatusPerCarrier.get(defaultSubId, false); mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(defaultSubId, isEntitled); } sendMessageDelayed(obtainMessage( EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT), WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS); case EVENT_WAIT_FOR_REGULAR_METRICS_REPORT_HYSTERESIS_TIMED_OUT: { handleEntireEntitlementMetricReport(); handleEntireProvisionMetricReport(); break; } Loading Loading @@ -4922,15 +4928,7 @@ public class SatelliteController extends Handler { synchronized (mSupportedSatelliteServicesLock) { if (mSatelliteEntitlementStatusPerCarrier.get(subId, false) != entitlementEnabled) { logd("update the carrier satellite enabled to " + entitlementEnabled); mSatelliteEntitlementStatusPerCarrier.put(subId, entitlementEnabled); mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(subId, entitlementEnabled); if (hasMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT)) { removeMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT); sendMessageDelayed(obtainMessage( EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT), WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS); } handleIndividualEntitlementMetricReport(subId, entitlementEnabled); try { mSubscriptionManagerService.setSubscriptionProperty(subId, SATELLITE_ENTITLEMENT_STATUS, entitlementEnabled ? "1" : "0"); Loading Loading @@ -5424,9 +5422,7 @@ public class SatelliteController extends Handler { updateCachedDeviceProvisionStatus(); // Report updated provisioned status to metrics. synchronized (mSatelliteTokenProvisionedLock) { boolean isProvisioned = !mProvisionedSubscriberId.isEmpty() && mProvisionedSubscriberId.containsValue(Boolean.TRUE); mControllerMetricsStats.setIsProvisioned(isProvisioned); handleEntireProvisionMetricReport(); } selectBindingSatelliteSubscription(false); evaluateCarrierRoamingNtnEligibilityChange(); Loading Loading @@ -6566,14 +6562,7 @@ public class SatelliteController extends Handler { entitlementStatus = "0"; } boolean result = entitlementStatus.equals("1"); mSatelliteEntitlementStatusPerCarrier.put(subId, result); mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(subId, result); if (hasMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT)) { removeMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT); sendMessageDelayed(obtainMessage( EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT), WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS); } handleIndividualEntitlementMetricReport(subId, result); } if (!mSatelliteEntitlementStatusPerCarrier.get(subId, false)) { Loading Loading @@ -9724,6 +9713,131 @@ public class SatelliteController extends Handler { return mWifiStateEnabled.get(); } @GuardedBy("mSupportedSatelliteServicesLock") private void handleEntireEntitlementMetricReport() { synchronized (mSupportedSatelliteServicesLock) { int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(true); if (activeSubIds != null && activeSubIds.length > 0) { for (int subId : activeSubIds) { boolean isSubIdEntitled = mSatelliteEntitlementStatusPerCarrier.get(subId); mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(subId, isSubIdEntitled); plogd("handleEntitlementMetricReport: subId=" + subId + ", isSubEntitled=" + isSubIdEntitled); } } else { loge("handleEntireEntitlementMetricReport: no active subId"); } } scheduleRegularMetricReportTimer(); } @GuardedBy("mSupportedSatelliteServicesLock") private void handleIndividualEntitlementMetricReport(int subId, boolean isSubscriptionEntitled) { synchronized (mSupportedSatelliteServicesLock) { mSatelliteEntitlementStatusPerCarrier.put(subId, isSubscriptionEntitled); mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(subId, isSubscriptionEntitled); } } @GuardedBy("mSatelliteTokenProvisionedLock") private void handleEntireProvisionMetricReport() { logd("handleEntireProvisionMetricReport:"); // Hold the final aggregated status for each carrierId. Map<Integer, CarrierReportInfo> reportDataPerCarrier = new HashMap<>(); synchronized (mSatelliteTokenProvisionedLock) { // Aggregate provision status and isNtnOnlyCarrier info per carrierId List<SubscriptionInfo> allSubInfos = mSubscriptionManagerService.getAllSubInfoList( mContext.getOpPackageName(), mContext.getAttributionTag()); for (SubscriptionInfo info : allSubInfos) { int subId = info.getSubscriptionId(); boolean isNtnOnlySubId = info.isOnlyNonTerrestrialNetwork(); boolean isActiveSubId = info.isActive(); if (!isNtnOnlySubId && !isActiveSubId) { plogd("handleEntireProvisionMetricReport: subId=" + subId + " is neither NTN-only nor active. Skipping."); continue; } int carrierId = SatelliteServiceUtils.getCarrierIdFromSubscription(subId); if (carrierId == TelephonyManager.UNKNOWN_CARRIER_ID && !isNtnOnlySubId) { plogd("handleEntireProvisionMetricReport: neither valid carrierId " + "nor NTN-only, subId=" + subId + ". Skipping."); continue; } String subscriberId = getSubscriberIdAndType(info).first; boolean isProvisioned = mProvisionedSubscriberId.getOrDefault(subscriberId, false); CarrierReportInfo carrierInfo = reportDataPerCarrier.computeIfAbsent( carrierId, key -> new CarrierReportInfo()); carrierInfo.aggregate(isProvisioned, isNtnOnlySubId); } } // Report the aggregated status for each carrierId if (reportDataPerCarrier.isEmpty()) { plogd("handleEntireProvisionMetricReport: No reportable data found"); } else { plogd("handleEntireProvisionMetricReport: Reporting final aggregated status for " + reportDataPerCarrier.size() + " carrier(s)."); for (Map.Entry<Integer, CarrierReportInfo> reportEntry : reportDataPerCarrier.entrySet()) { int carrierId = reportEntry.getKey(); CarrierReportInfo info = reportEntry.getValue(); plogd("handleEntireProvisionMetricReport: Final report for carrierId=" + carrierId + ", isProvisioned=" + info.mIsAnySubProvisioned + ", isNtnOnlyCarrier=" + info.mIsNtnOnlyCarrier); mControllerMetricsStats.setIsProvisioned( carrierId, info.mIsAnySubProvisioned, info.mIsNtnOnlyCarrier); } } scheduleRegularMetricReportTimer(); } // Helper class to store aggregated information per carrierId. private static class CarrierReportInfo { boolean mIsAnySubProvisioned = false; boolean mIsNtnOnlyCarrier = false; void aggregate(boolean isProvisioned, boolean isNtnOnlyCarrier) { // if any subId for the carrier was provisioned, this carrier is provisioned if (isProvisioned) { this.mIsAnySubProvisioned = true; } this.mIsNtnOnlyCarrier = isNtnOnlyCarrier; } } private void scheduleRegularMetricReportTimer() { if (mAlarmManager == null) { plogd("scheduleRegularMetricReportTimer: AlarmManager is null"); return; } mAlarmManager.cancel(mRegularMetricReportAlarmListener); mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, getElapsedRealtime() + REGULAR_METRIC_REPORTING_INTERVAL_MILLIS, TAG, new HandlerExecutor(this), new WorkSource(), mRegularMetricReportAlarmListener); } /** * Uses this function to set AlarmManager object for testing. * * @param alarmManager The instance of AlarmManager. */ @VisibleForTesting public void setAlarmManager(AlarmManager alarmManager) { mAlarmManager = alarmManager; } /** * Method to return the current data plan for the registered plmn based on entitlement * provisioning information. Note: If no information at Loading
src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java +8 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.telephony.PersistentLogger; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.satellite.AntennaPosition; import android.telephony.satellite.EarfcnRange; import android.telephony.satellite.NtnSignalStrength; Loading Loading @@ -738,6 +739,13 @@ public class SatelliteServiceUtils { return satelliteController.isInCarrierRoamingNbIotNtn(phone); } /** Returns the carrier ID of the given subscription id. */ public static int getCarrierIdFromSubscription(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); Phone phone = PhoneFactory.getPhone(phoneId); return phone != null ? phone.getCarrierId() : TelephonyManager.UNKNOWN_CARRIER_ID; } private static void logd(@NonNull String log) { Log.d(TAG, log); } Loading
src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java +4 −3 Original line number Diff line number Diff line Loading @@ -390,12 +390,13 @@ public class ControllerMetricsStats { } /** Capture the latest provisioned state for satellite service */ @VisibleForTesting public void setIsProvisioned(boolean isProvisioned) { logd("setIsProvisioned:" + isProvisioned); public void setIsProvisioned(int carrierId, boolean isProvisioned, boolean isNtnOnlyCarrier) { logd("setIsProvisioned: carrierId=" + carrierId + ", isProvisioned=" + isProvisioned); mSatelliteStats.onSatelliteControllerMetrics( new SatelliteStats.SatelliteControllerParams.Builder() .setCarrierId(carrierId) .setIsProvisioned(isProvisioned) .setIsNtnOnlyCarrier(isNtnOnlyCarrier) .build()); } Loading
tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java +67 −1 Original line number Diff line number Diff line Loading @@ -108,6 +108,7 @@ import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; Loading @@ -122,6 +123,7 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.NotificationManager; import android.app.usage.NetworkStatsManager; import android.content.BroadcastReceiver; Loading @@ -146,6 +148,7 @@ import android.os.OutcomeReceiver; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.WorkSource; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; Loading Loading @@ -209,6 +212,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; Loading Loading @@ -290,6 +294,10 @@ public class SatelliteControllerTest extends TelephonyTest { @Mock private SubscriptionInfo mSubscriptionInfo; @Mock private PackageManager mMockPManager; @Mock private Intent mMockLocationIntent; @Mock private AlarmManager mMockAlarmManager; @Captor private ArgumentCaptor<AlarmManager.OnAlarmListener> mAlarmListenerCaptor; private Semaphore mIIntegerConsumerSemaphore = new Semaphore(0); private IIntegerConsumer mIIntegerConsumer = new IIntegerConsumer.Stub() { Loading Loading @@ -736,6 +744,10 @@ public class SatelliteControllerTest extends TelephonyTest { doReturn(true).when(mFeatureFlags).satelliteImproveMultiThreadDesign(); doReturn(TEST_ALL_SATELLITE_PLMN_SET).when(mMockSatelliteController).getAllPlmnSet(); mSatelliteControllerUT.setAlarmManager(mMockAlarmManager); doNothing().when(mMockAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class)); doNothing().when(mMockAlarmManager).setExact(anyInt(), anyLong(), anyString(), any(Executor.class), any(WorkSource.class), mAlarmListenerCaptor.capture()); } @After Loading Loading @@ -1763,7 +1775,7 @@ public class SatelliteControllerTest extends TelephonyTest { } @Test public void testRegisterForSatelliteProvisionStateChanged() { public void testRegisterForSatelliteProvisionStateChanged() throws Exception { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); Semaphore semaphore = new Semaphore(0); ISatelliteProvisionStateCallback callback = Loading Loading @@ -4446,9 +4458,63 @@ public class SatelliteControllerTest extends TelephonyTest { @Test public void testProvisionSatellite() throws Exception { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); when(mFeatureFlags.satelliteImproveMultiThreadDesign()).thenReturn(true); verifyRequestSatelliteSubscriberProvisionStatus(); List<SatelliteSubscriberInfo> inputList = getExpectedSatelliteSubscriberInfoList(); try { replaceInstance(SatelliteController.class, "sInstance", null, mSatelliteControllerUT); } catch (Exception ex) { loge(ex.toString()); } reset(mMockControllerMetricsStats); reset(mMockAlarmManager); doNothing().when(mMockAlarmManager).setExact(anyInt(), anyLong(), anyString(), any(Executor.class), any(WorkSource.class), mAlarmListenerCaptor.capture()); verifyProvisionSatellite(inputList); int numberOfCarriers = (int) inputList.stream() .map(SatelliteSubscriberInfo::getCarrierId) .distinct() .count(); // TODO b/409584433 for now handleRequestProvisionSatellite is invoked 2 times. // expectedMetricReportCallCount should be restore to numberOfCarriers eventually. int expectedMetricReportCallCount = numberOfCarriers; if (mFeatureFlags.satelliteImproveMultiThreadDesign()) { expectedMetricReportCallCount *= 2; } verify(mMockControllerMetricsStats, times(expectedMetricReportCallCount)).setIsProvisioned( anyInt(), eq(true), anyBoolean()); verify(mMockAlarmManager, atLeastOnce()).cancel(any(AlarmManager.OnAlarmListener.class)); verify(mMockAlarmManager, atLeastOnce()).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(), any(Executor.class), any(WorkSource.class), any(AlarmManager.OnAlarmListener.class)); AlarmManager.OnAlarmListener capturedListener = mAlarmListenerCaptor.getValue(); if (capturedListener == null) { fail("AlarmListener was not captured by AlarmManager.setExact()"); } final CountDownLatch countDownLatch = new CountDownLatch(numberOfCarriers); doAnswer(invocation -> { countDownLatch.countDown(); return null; }).when(mMockControllerMetricsStats).setIsProvisioned(anyInt(), anyBoolean(), anyBoolean()); capturedListener.onAlarm(); processAllMessages(); try { if (!countDownLatch.await(2, TimeUnit.SECONDS)) { fail("Handler did not process the expected message (latch timed out)"); } } catch (InterruptedException ex) { loge(ex.toString()); } expectedMetricReportCallCount += numberOfCarriers; verify(mMockControllerMetricsStats, times(expectedMetricReportCallCount)).setIsProvisioned( anyInt(), eq(true), anyBoolean()); verify(mMockAlarmManager, atLeast(2)).cancel(any(AlarmManager.OnAlarmListener.class)); verify(mMockAlarmManager, atLeast(2)).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(), any(Executor.class), any(WorkSource.class), any(AlarmManager.OnAlarmListener.class)); } private void verifyProvisionSatellite(List<SatelliteSubscriberInfo> inputList) { Loading