diff options
author | Rafael Higuera Silva <rafahs@google.com> | 2023-04-25 20:08:02 +0000 |
---|---|---|
committer | Rafael Higuera Silva <rafahs@google.com> | 2023-05-11 17:59:28 +0000 |
commit | 7fcf5f958b7e848a9ac994abf77c17292c2e786a (patch) | |
tree | 1e4ec43b85ef01e6a75eaf9c73b913178c2e9513 | |
parent | d92836c656539cede8d6deaa1cdb917799f28a73 (diff) | |
download | telephony-7fcf5f958b7e848a9ac994abf77c17292c2e786a.tar.gz |
Add fold state to service state
Bug: 276772849
Test: make, atest com.android.internal.telephony.metrics and manual test
Change-Id: I62ff6fb1be3aef6ac9044990b78094025da1da73
Merged-In: I62ff6fb1be3aef6ac9044990b78094025da1da73
8 files changed, 174 insertions, 9 deletions
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto index aa784dd5c1..fc3f0d0e9c 100644 --- a/proto/src/persist_atoms.proto +++ b/proto/src/persist_atoms.proto @@ -274,6 +274,7 @@ message VoiceCallSession { optional bool is_multiparty = 31; optional int32 call_duration = 32; optional int32 last_known_rat = 33; + // Internal use only optional int64 setup_begin_millis = 10001; } @@ -375,6 +376,7 @@ message CellularServiceState { optional int64 total_time_millis = 9; // Duration needs to be rounded when pulled optional bool is_emergency_only = 10; optional bool is_internet_pdn_up = 11; + optional int32 fold_state = 12; // Internal use only optional int64 last_used_millis = 10001; diff --git a/src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java b/src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java new file mode 100644 index 0000000000..29729c8092 --- /dev/null +++ b/src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.metrics; + +import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED; +import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_FLIPPED; +import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_HALF_OPENED; +import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_OPENED; +import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN; + +import android.content.Context; +import android.hardware.devicestate.DeviceStateManager; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.HandlerThread; + +import com.android.internal.telephony.Phone; + +/** Device state information like the fold state. */ +public class DeviceStateHelper { + private int mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN; + + public DeviceStateHelper(Context context) { + HandlerThread mHandlerThread = new HandlerThread("DeviceStateHelperThread"); + mHandlerThread.start(); + context.getSystemService(DeviceStateManager.class) + .registerCallback( + new HandlerExecutor(new Handler(mHandlerThread.getLooper())), + state -> { + updateFoldState(state); + }); + } + + private void updateFoldState(int posture) { + switch (posture) { + case 0: + mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED; + break; + case 1: + mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_HALF_OPENED; + break; + case 2: + mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_OPENED; + break; + case 4: + mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_FLIPPED; + break; + default: + mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN; + } + updateServiceStateStats(); + } + + private void updateServiceStateStats() { + for (Phone phone : MetricsCollector.getPhonesIfAny()) { + phone.getServiceStateTracker().getServiceStateStats().onFoldStateChanged(mFoldState); + } + } + + public int getFoldState() { + return mFoldState; + } +} diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java index 7546124697..aab55e6d39 100644 --- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java +++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java @@ -145,20 +145,22 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { DBG ? 2L * MILLIS_PER_SECOND : 5L * MILLIS_PER_MINUTE; private final PersistAtomsStorage mStorage; + private final DeviceStateHelper mDeviceStateHelper; private final StatsManager mStatsManager; private final AirplaneModeStats mAirplaneModeStats; private final Set<DataCallSessionStats> mOngoingDataCallStats = ConcurrentHashMap.newKeySet(); private static final Random sRandom = new Random(); public MetricsCollector(Context context) { - this(context, new PersistAtomsStorage(context)); + this(context, new PersistAtomsStorage(context), new DeviceStateHelper(context)); } /** Allows dependency injection. Used during unit tests. */ @VisibleForTesting - public MetricsCollector(Context context, - PersistAtomsStorage storage) { + public MetricsCollector( + Context context, PersistAtomsStorage storage, DeviceStateHelper deviceStateHelper) { mStorage = storage; + mDeviceStateHelper = deviceStateHelper; mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER); if (mStatsManager != null) { // Most (but not all) of these are subject to cooldown specified by MIN_COOLDOWN_MILLIS. @@ -299,6 +301,11 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { return mStorage; } + /** Returns the {@link DeviceStateHelper}. */ + public DeviceStateHelper getDeviceStateHelper() { + return mDeviceStateHelper; + } + /** Updates duration segments and calls {@link PersistAtomsStorage#flushAtoms()}. */ public void flushAtomsStorage() { concludeAll(); @@ -915,7 +922,8 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { state.carrierId, roundAndConvertMillisToSeconds(state.totalTimeMillis), state.isEmergencyOnly, - state.isInternetPdnUp); + state.isInternetPdnUp, + state.foldState); } private static StatsEvent buildStatsEvent(VoiceCallRatUsage usage) { @@ -1316,7 +1324,7 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { } /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */ - private static Phone[] getPhonesIfAny() { + static Phone[] getPhonesIfAny() { try { return PhoneFactory.getPhones(); } catch (IllegalStateException e) { diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java index 13ba91b269..5a21bafd49 100644 --- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java +++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java @@ -1708,7 +1708,8 @@ public class PersistAtomsStorage { && state.isMultiSim == key.isMultiSim && state.carrierId == key.carrierId && state.isEmergencyOnly == key.isEmergencyOnly - && state.isInternetPdnUp == key.isInternetPdnUp) { + && state.isInternetPdnUp == key.isInternetPdnUp + && state.foldState == key.foldState) { return state; } } @@ -2334,4 +2335,4 @@ public class PersistAtomsStorage { // Epoch time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP return System.currentTimeMillis(); } -}
\ No newline at end of file +} diff --git a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java index 7c63dec3a3..b830cd00d2 100644 --- a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java +++ b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java @@ -54,11 +54,13 @@ public class ServiceStateStats extends DataNetworkControllerCallback { new AtomicReference<>(new TimestampedServiceState(null, 0L)); private final Phone mPhone; private final PersistAtomsStorage mStorage; + private final DeviceStateHelper mDeviceStateHelper; public ServiceStateStats(Phone phone) { super(Runnable::run); mPhone = phone; mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage(); + mDeviceStateHelper = PhoneFactory.getMetricsCollector().getDeviceStateHelper(); } /** Finalizes the durations of the current service state segment. */ @@ -120,6 +122,7 @@ public class ServiceStateStats extends DataNetworkControllerCallback { newState.carrierId = mPhone.getCarrierId(); newState.isEmergencyOnly = isEmergencyOnly(serviceState); newState.isInternetPdnUp = isInternetPdnUp(mPhone); + newState.foldState = mDeviceStateHelper.getFoldState(); TimestampedServiceState prevState = mLastState.getAndSet(new TimestampedServiceState(newState, now)); addServiceStateAndSwitch( @@ -127,6 +130,26 @@ public class ServiceStateStats extends DataNetworkControllerCallback { } } + /** Updates the fold state of the device for the current service state. */ + public void onFoldStateChanged(int foldState) { + final long now = getTimeMillis(); + CellularServiceState lastServiceState = mLastState.get().mServiceState; + if (lastServiceState == null || lastServiceState.foldState == foldState) { + // Not need to update the fold state if modem is off or if is the + // same fold state + return; + } else { + TimestampedServiceState lastState = + mLastState.getAndUpdate( + state -> { + CellularServiceState newServiceState = copyOf(state.mServiceState); + newServiceState.foldState = foldState; + return new TimestampedServiceState(newServiceState, now); + }); + addServiceState(lastState, now); + } + } + private void addServiceState(TimestampedServiceState prevState, long now) { addServiceStateAndSwitch(prevState, now, null); } @@ -247,6 +270,7 @@ public class ServiceStateStats extends DataNetworkControllerCallback { copy.totalTimeMillis = state.totalTimeMillis; copy.isEmergencyOnly = state.isEmergencyOnly; copy.isInternetPdnUp = state.isInternetPdnUp; + copy.foldState = state.foldState; return copy; } diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java index 2f56f2ad90..b044814765 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java @@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN; + import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyString; @@ -109,6 +111,7 @@ import com.android.internal.telephony.imsphone.ImsExternalCallTracker; import com.android.internal.telephony.imsphone.ImsNrSaModeHandler; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; +import com.android.internal.telephony.metrics.DeviceStateHelper; import com.android.internal.telephony.metrics.ImsStats; import com.android.internal.telephony.metrics.MetricsCollector; import com.android.internal.telephony.metrics.PersistAtomsStorage; @@ -269,6 +272,7 @@ public abstract class TelephonyTest { protected DataServiceManager mMockedWlanDataServiceManager; protected ServiceStateStats mServiceStateStats; protected SatelliteController mSatelliteController; + protected DeviceStateHelper mDeviceStateHelper; // Initialized classes protected ActivityManager mActivityManager; @@ -504,6 +508,7 @@ public abstract class TelephonyTest { mMockedWlanDataServiceManager = Mockito.mock(DataServiceManager.class); mServiceStateStats = Mockito.mock(ServiceStateStats.class); mSatelliteController = Mockito.mock(SatelliteController.class); + mDeviceStateHelper = Mockito.mock(DeviceStateHelper.class); TelephonyManager.disableServiceHandleCaching(); PropertyInvalidatedCache.disableForTestMode(); @@ -828,6 +833,11 @@ public abstract class TelephonyTest { doReturn(null).when(mContext).getFileStreamPath(anyString()); doReturn(mPersistAtomsStorage).when(mMetricsCollector).getAtomsStorage(); doReturn(mWifiManager).when(mContext).getSystemService(eq(Context.WIFI_SERVICE)); + doReturn(mDeviceStateHelper).when(mMetricsCollector).getDeviceStateHelper(); + doReturn(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN) + .when(mDeviceStateHelper) + .getFoldState(); + doReturn(null).when(mContext).getSystemService(eq(Context.DEVICE_STATE_SERVICE)); //Use reflection to mock singletons replaceInstance(CallManager.class, "INSTANCE", null, mCallManager); diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java index 58864229e7..d4e1b86cda 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java @@ -104,7 +104,7 @@ public class MetricsCollectorTest extends TelephonyTest { mActivePort = mock(UiccPort.class); mServiceStateStats = mock(ServiceStateStats.class); mMetricsCollector = - new MetricsCollector(mContext, mPersistAtomsStorage); + new MetricsCollector(mContext, mPersistAtomsStorage, mDeviceStateHelper); doReturn(mSST).when(mSecondPhone).getServiceStateTracker(); doReturn(mServiceStateStats).when(mSST).getServiceStateStats(); } @@ -455,4 +455,4 @@ public class MetricsCollectorTest extends TelephonyTest { assertThat(actualAtoms).hasSize(4); assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS); } -}
\ No newline at end of file +} diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java index 8885aa4f59..7b66a52e40 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java @@ -19,6 +19,8 @@ package com.android.internal.telephony.metrics; import static android.telephony.TelephonyManager.DATA_CONNECTED; import static android.telephony.TelephonyManager.DATA_UNKNOWN; +import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED; +import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN; import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN; @@ -993,6 +995,47 @@ public class ServiceStateStatsTest extends TelephonyTest { mPhone, mServiceState, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN)); } + @Test + @SmallTest + public void onFoldStateChanged_modemOff() throws Exception { + doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getVoiceRegState(); + doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getDataRegState(); + doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType(); + doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType(); + mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN); + doReturn(-1).when(mPhone).getCarrierId(); + mServiceStateStats.onServiceStateChanged(mServiceState); + mServiceStateStats.incTimeMillis(100L); + + mServiceStateStats.onFoldStateChanged(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED); + verifyNoMoreInteractions(mPersistAtomsStorage); + } + + @Test + @SmallTest + public void onFoldStateChanged_LTEMode() throws Exception { + // Using default service state for LTE with fold state unknown + mServiceStateStats.onServiceStateChanged(mServiceState); + mServiceStateStats.incTimeMillis(100L); + mServiceStateStats.onFoldStateChanged(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED); + mServiceStateStats.incTimeMillis(1000L); + // Same fold state as before should not generate a new atom + mServiceStateStats.onFoldStateChanged(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED); + mServiceStateStats.incTimeMillis(1000L); + + // There should be 2 service state updates + mServiceStateStats.conclude(); + ArgumentCaptor<CellularServiceState> captor = + ArgumentCaptor.forClass(CellularServiceState.class); + verify(mPersistAtomsStorage, times(2)) + .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null)); + CellularServiceState state = captor.getAllValues().get(0); + assertEquals(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN, state.foldState); + state = captor.getAllValues().get(1); + assertEquals(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED, state.foldState); + verifyNoMoreInteractions(mPersistAtomsStorage); + } + private void mockWwanPsRat(@NetworkType int rat) { mockWwanRat( NetworkRegistrationInfo.DOMAIN_PS, |