Loading proto/src/persist_atoms.proto +2 −0 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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; Loading src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * 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; } } src/java/com/android/internal/telephony/metrics/MetricsCollector.java +13 −5 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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(); Loading Loading @@ -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) { Loading Loading @@ -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) { Loading src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java +3 −2 Original line number Diff line number Diff line Loading @@ -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; } } Loading src/java/com/android/internal/telephony/metrics/ServiceStateStats.java +24 −0 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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( Loading @@ -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); } Loading Loading @@ -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; } Loading Loading
proto/src/persist_atoms.proto +2 −0 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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; Loading
src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * 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; } }
src/java/com/android/internal/telephony/metrics/MetricsCollector.java +13 −5 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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(); Loading Loading @@ -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) { Loading Loading @@ -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) { Loading
src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java +3 −2 Original line number Diff line number Diff line Loading @@ -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; } } Loading
src/java/com/android/internal/telephony/metrics/ServiceStateStats.java +24 −0 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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( Loading @@ -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); } Loading Loading @@ -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; } Loading