diff options
author | Manish Dungriyal <mdungriyal@google.com> | 2023-01-27 12:19:13 +0000 |
---|---|---|
committer | Manish Dungriyal <mdungriyal@google.com> | 2023-04-05 05:26:25 +0000 |
commit | ccc00093b1e6240d47902040328c04205ace9e2d (patch) | |
tree | 84db1837cb830058111fd526fe6334f233b437b1 | |
parent | 27d15968aa33335e73cb95f59f62e2aa452d3381 (diff) | |
download | Telephony-ccc00093b1e6240d47902040328c04205ace9e2d.tar.gz |
QnsTimer implementation for delayed events in QNS
Delayed messages in QNS needs to handle such that they will be
unimpacted even in doze mode.
Bug: 258753685
Test: atest QualifiedNetworksServiceTests and manual testing.
Change-Id: I65f95069e7a8099ba8c80c7a25e7963ab79e9a6a
Merged-In: I65f95069e7a8099ba8c80c7a25e7963ab79e9a6a
(cherry picked from commit 1a9a04b3658a785704e5d6f68eaa6d915f21cd2d)
16 files changed, 950 insertions, 91 deletions
diff --git a/services/QualifiedNetworksService/AndroidManifest.xml b/services/QualifiedNetworksService/AndroidManifest.xml index 9b2444b..5abdbf9 100644 --- a/services/QualifiedNetworksService/AndroidManifest.xml +++ b/services/QualifiedNetworksService/AndroidManifest.xml @@ -27,6 +27,7 @@ <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> <uses-permission android:name="android.permission.REGISTER_STATS_PULL_ATOM" /> + <uses-permission android:name="android.permission.USE_EXACT_ALARM"/> <application android:directBootAware="true" android:defaultToDeviceProtectedStorage="true"> diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCallStatusTracker.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCallStatusTracker.java index f1b6e37..f8b335a 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCallStatusTracker.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCallStatusTracker.java @@ -16,6 +16,8 @@ package com.android.telephony.qns; +import static com.android.telephony.qns.QnsConstants.INVALID_ID; + import android.annotation.NonNull; import android.net.NetworkCapabilities; import android.os.Handler; @@ -50,6 +52,7 @@ public class QnsCallStatusTracker { private List<CallState> mCallStates = new ArrayList<>(); private QnsRegistrant mCallTypeChangedEventListener; private QnsRegistrant mEmergencyCallTypeChangedEventListener; + private final QnsTimer mQnsTimer; private int mLastNormalCallType = QnsConstants.CALL_TYPE_IDLE; private int mLastEmergencyCallType = QnsConstants.CALL_TYPE_IDLE; private boolean mEmergencyOverIms; @@ -144,7 +147,6 @@ public class QnsCallStatusTracker { private static final int EVENT_PACKET_LOSS_TIMER_EXPIRED = 3402; private static final int EVENT_HYSTERESIS_FOR_NORMAL_QUALITY = 3403; private static final int EVENT_POLLING_CHECK_LOW_QUALITY = 3404; - private static final int EVENT_LOW_QUALITY_HANDLER_MAX = 3405; private static final int STATE_NORMAL_QUALITY = 0; private static final int STATE_SUSPECT_LOW_QUALITY = 1; @@ -156,6 +158,9 @@ public class QnsCallStatusTracker { private static final int LOW_QUALITY_REPORTED_TIME_INITIAL_VALUE = -1; private int mState = STATE_NORMAL_QUALITY; + private int mPacketLossTimerId = INVALID_ID; + private int mHysteresisTimerId = INVALID_ID; + private int mPollingCheckTimerId = INVALID_ID; private MediaQualityStatus mMediaQualityStatus; private String mTag; @@ -204,12 +209,14 @@ public class QnsCallStatusTracker { return; } else { // check normal quality is stable or not. - this.sendEmptyMessageDelayed(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY, + mHysteresisTimerId = mQnsTimer.registerTimer( + Message.obtain(this, EVENT_HYSTERESIS_FOR_NORMAL_QUALITY), HYSTERESIS_TIME_NORMAL_QUALITY_MILLIS); } } else { // Threshold breached. - this.removeMessages(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY); + mQnsTimer.unregisterTimer(mHysteresisTimerId); + mHysteresisTimerId = INVALID_ID; switch (mState) { case STATE_NORMAL_QUALITY: case STATE_SUSPECT_LOW_QUALITY: @@ -223,7 +230,8 @@ public class QnsCallStatusTracker { needNotify = true; } } else { - removeMessages(EVENT_PACKET_LOSS_TIMER_EXPIRED); + mQnsTimer.unregisterTimer(mPacketLossTimerId); + mPacketLossTimerId = INVALID_ID; enterLowQualityState(status); needNotify = true; } @@ -250,28 +258,30 @@ public class QnsCallStatusTracker { void enterLowQualityState(MediaQualityStatus status) { Log.d(mTag, "enterLowQualityState " + status); mState = STATE_LOW_QUALITY; - this.sendEmptyMessageDelayed( - EVENT_POLLING_CHECK_LOW_QUALITY, LOW_QUALITY_CHECK_INTERVAL_MILLIS); + mPollingCheckTimerId = mQnsTimer.registerTimer( + Message.obtain(this, EVENT_POLLING_CHECK_LOW_QUALITY), + LOW_QUALITY_CHECK_INTERVAL_MILLIS); } void enterSuspectLowQualityState(int delayMillis) { Log.d(mTag, "enterSuspectLowQualityState."); - if (!this.hasMessages(EVENT_PACKET_LOSS_TIMER_EXPIRED)) { - this.removeMessages(EVENT_PACKET_LOSS_TIMER_EXPIRED); - } + mQnsTimer.unregisterTimer(mPacketLossTimerId); Log.d(mTag, "Packet loss timer start. " + delayMillis); Message msg = this.obtainMessage( EVENT_PACKET_LOSS_TIMER_EXPIRED, mTransportType, 0); - this.sendMessageDelayed(msg, delayMillis); + mPacketLossTimerId = mQnsTimer.registerTimer(msg, delayMillis); mState = STATE_SUSPECT_LOW_QUALITY; } void exitLowQualityState() { mState = STATE_NORMAL_QUALITY; - for (int i = EVENT_PACKET_LOSS_TIMER_EXPIRED; - i < EVENT_LOW_QUALITY_HANDLER_MAX; i++) { - this.removeMessages(i); - } + this.removeCallbacksAndMessages(null); + mQnsTimer.unregisterTimer(mPacketLossTimerId); + mQnsTimer.unregisterTimer(mHysteresisTimerId); + mQnsTimer.unregisterTimer(mPollingCheckTimerId); + mPacketLossTimerId = INVALID_ID; + mHysteresisTimerId = INVALID_ID; + mPollingCheckTimerId = INVALID_ID; notifyLowMediaQuality(0); } @@ -283,9 +293,10 @@ public class QnsCallStatusTracker { int reason = thresholdBreached(mMediaQualityStatus); if (reason > 0) { notifyLowMediaQuality(thresholdBreached(mMediaQualityStatus)); - } else if (this.hasMessages(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY)) { + } else if (mHysteresisTimerId != INVALID_ID) { // hysteresis time to be normal state is running. let's check after that. - this.sendEmptyMessageDelayed(EVENT_POLLING_CHECK_LOW_QUALITY, + mPollingCheckTimerId = mQnsTimer.registerTimer( + Message.obtain(this, EVENT_POLLING_CHECK_LOW_QUALITY), HYSTERESIS_TIME_NORMAL_QUALITY_MILLIS); } else { Log.w(mTag, "Unexpected case."); @@ -296,19 +307,22 @@ public class QnsCallStatusTracker { void updateForHandover(int transportType) { // restart timers that they need to be restarted on new transport type. if (mState == STATE_SUSPECT_LOW_QUALITY) { - this.removeMessages(EVENT_PACKET_LOSS_TIMER_EXPIRED); + mQnsTimer.unregisterTimer(mPacketLossTimerId); Message msg = this.obtainMessage( EVENT_PACKET_LOSS_TIMER_EXPIRED, transportType, 0); - this.sendMessageDelayed(msg, (mConfigManager.getRTPMetricsData()).mPktLossTime); + mPacketLossTimerId = mQnsTimer.registerTimer(msg, + (mConfigManager.getRTPMetricsData()).mPktLossTime); } - if (this.hasMessages(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY)) { - this.removeMessages(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY); - this.sendEmptyMessageDelayed(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY, + if (mHysteresisTimerId != INVALID_ID) { + mQnsTimer.unregisterTimer(mHysteresisTimerId); + mHysteresisTimerId = mQnsTimer.registerTimer( + Message.obtain(this, EVENT_HYSTERESIS_FOR_NORMAL_QUALITY), HYSTERESIS_TIME_NORMAL_QUALITY_MILLIS); } if (mState == STATE_LOW_QUALITY) { - this.removeMessages(EVENT_POLLING_CHECK_LOW_QUALITY); - this.sendEmptyMessageDelayed(EVENT_POLLING_CHECK_LOW_QUALITY, + mQnsTimer.unregisterTimer(mPollingCheckTimerId); + mPollingCheckTimerId = mQnsTimer.registerTimer( + Message.obtain(this, EVENT_POLLING_CHECK_LOW_QUALITY), LOW_QUALITY_CHECK_AFTER_HO_MILLIS); } } @@ -727,17 +741,19 @@ public class QnsCallStatusTracker { } QnsCallStatusTracker(QnsTelephonyListener telephonyListener, - QnsCarrierConfigManager configManager, int slotIndex) { - this(telephonyListener, configManager, slotIndex, null); + QnsCarrierConfigManager configManager, QnsTimer qnsTimer, int slotIndex) { + this(telephonyListener, configManager, qnsTimer, slotIndex, null); } /** Only for test */ @VisibleForTesting QnsCallStatusTracker(QnsTelephonyListener telephonyListener, - QnsCarrierConfigManager configManager, int slotIndex, Looper looper) { + QnsCarrierConfigManager configManager, QnsTimer qnsTimer, int slotIndex, + Looper looper) { mLogTag = QnsCallStatusTracker.class.getSimpleName() + "_" + slotIndex; mTelephonyListener = telephonyListener; mConfigManager = configManager; + mQnsTimer = qnsTimer; mActiveCallTracker = new ActiveCallTracker(slotIndex, looper); mTelephonyListener.addCallStatesChangedCallback(mCallStatesConsumer); mTelephonyListener.addSrvccStateChangedCallback(mSrvccStateConsumer); @@ -842,6 +858,7 @@ public class QnsCallStatusTracker { } else { mActiveCallTracker.callStarted(callType, netCapability); } + mQnsTimer.updateCallState(callType); } boolean isCallIdle() { diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsComponents.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsComponents.java index 04780e7..5862bc2 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsComponents.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsComponents.java @@ -22,6 +22,7 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -44,6 +45,7 @@ class QnsComponents { private final SparseArray<WifiBackhaulMonitor> mWifiBackhaulMonitors; private final List<Integer> mSlotIds; private IwlanNetworkStatusTracker mIwlanNetworkStatusTracker; + private QnsTimer mQnsTimer; private WifiQualityMonitor mWifiQualityMonitor; private QnsMetrics mQnsMetrics; @@ -88,20 +90,26 @@ class QnsComponents { mQnsCarrierConfigManagers.put( slotId, new QnsCarrierConfigManager(mContext, mQnsEventDispatchers.get(slotId), slotId)); + if (mQnsTimer == null) { + mQnsTimer = new QnsTimer(mContext); + } mQnsCallStatusTracker.put( slotId, - new QnsCallStatusTracker(mQnsTelephonyListeners.get(slotId), - mQnsCarrierConfigManagers.get(slotId), slotId)); + new QnsCallStatusTracker( + mQnsTelephonyListeners.get(slotId), + mQnsCarrierConfigManagers.get(slotId), + mQnsTimer, + slotId)); mWifiBackhaulMonitors.put( slotId, new WifiBackhaulMonitor( mContext, mQnsCarrierConfigManagers.get(slotId), mQnsImsManagers.get(slotId), + mQnsTimer, slotId)); - if (mWifiQualityMonitor == null) { - mWifiQualityMonitor = new WifiQualityMonitor(mContext); + mWifiQualityMonitor = new WifiQualityMonitor(mContext, mQnsTimer); } if (mIwlanNetworkStatusTracker == null) { mIwlanNetworkStatusTracker = new IwlanNetworkStatusTracker(mContext); @@ -131,6 +139,7 @@ class QnsComponents { QnsProvisioningListener qnsProvisioningListener, QnsTelephonyListener qnsTelephonyListener, QnsCallStatusTracker qnsCallStatusTracker, + QnsTimer qnsTimer, WifiBackhaulMonitor wifiBackhaulMonitor, WifiQualityMonitor wifiQualityMonitor, QnsMetrics qnsMetrics, @@ -149,6 +158,7 @@ class QnsComponents { mWifiBackhaulMonitors.put(slotId, wifiBackhaulMonitor); mWifiQualityMonitor = wifiQualityMonitor; + mQnsTimer = qnsTimer; mIwlanNetworkStatusTracker = iwlanNetworkStatusTracker; mIwlanNetworkStatusTracker.initBySlotIndex( qnsCarrierConfigManager, @@ -214,6 +224,11 @@ class QnsComponents { return mWifiQualityMonitor; } + /** Returns instance of QnsTimer. */ + QnsTimer getQnsTimer() { + return mQnsTimer; + } + /** Returns instance of WifiQualityMonitor. */ QnsMetrics getQnsMetrics() { return mQnsMetrics; @@ -247,6 +262,10 @@ class QnsComponents { mQnsCallStatusTracker.remove(slotId); qnsCallStatusTracker.close(); } + if (mSlotIds.size() == 1) { + mQnsTimer.close(); + mQnsTimer = null; + } QnsCarrierConfigManager qnsCarrierConfigManager = mQnsCarrierConfigManagers.get(slotId); if (qnsCarrierConfigManager != null) { mQnsCarrierConfigManagers.remove(slotId); @@ -286,4 +305,16 @@ class QnsComponents { mSlotIds.remove(Integer.valueOf(slotId)); Log.d(mLogTag, "QnsComponents closed for slot " + slotId); } + + void dump(PrintWriter pw) { + if (mIwlanNetworkStatusTracker != null) { + mIwlanNetworkStatusTracker.dump(pw, " "); + } + if (mIwlanNetworkStatusTracker != null) { + mWifiQualityMonitor.dump(pw, " "); + } + if (mIwlanNetworkStatusTracker != null) { + mQnsTimer.dump(pw, " "); + } + } } diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTimer.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTimer.java new file mode 100644 index 0000000..f2439bd --- /dev/null +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTimer.java @@ -0,0 +1,415 @@ +/* + * 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.telephony.qns; + +import static com.android.telephony.qns.QnsConstants.CALL_TYPE_IDLE; +import static com.android.telephony.qns.QnsConstants.INVALID_ID; +import static com.android.telephony.qns.QnsUtils.getSystemElapsedRealTime; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import android.os.PowerManager; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.PrintWriter; +import java.util.Comparator; +import java.util.Objects; +import java.util.PriorityQueue; +import java.util.concurrent.atomic.AtomicInteger; + +/** This class handles the delayed events triggered in QNS. */ +class QnsTimer { + + private static final String TAG = QnsTimer.class.getSimpleName(); + private static final int EVENT_QNS_TIMER_EXPIRED = 1; + static final String ACTION_ALARM_TIMER_EXPIRED = + "com.android.telephony.qns.action.ALARM_TIMER_EXPIRED"; + + private static final AtomicInteger sTimerId = new AtomicInteger(); + private final Context mContext; + private final AlarmManager mAlarmManager; + private final PowerManager mPowerManager; + private final HandlerThread mHandlerThread; + private final BroadcastReceiver mBroadcastReceiver; + private final PriorityQueue<TimerInfo> mTimerInfos; + private PendingIntent mPendingIntent; + private long mMinAlarmTimeMs = 10000; + private int mCurrentAlarmTimerId = INVALID_ID; + private int mCurrentHandlerTimerId = INVALID_ID; + private boolean mIsAlarmRequired; + @VisibleForTesting final Handler mHandler; + private long mLastAlarmTriggerAtMs = Long.MAX_VALUE; + private int mCallType = CALL_TYPE_IDLE; + + QnsTimer(Context context) { + mContext = context; + mAlarmManager = mContext.getSystemService(AlarmManager.class); + mPowerManager = mContext.getSystemService(PowerManager.class); + mBroadcastReceiver = new AlarmReceiver(); + mTimerInfos = + new PriorityQueue<>(Comparator.comparingLong(TimerInfo::getExpireAtElapsedMillis)); + mHandlerThread = new HandlerThread(TAG); + mHandlerThread.start(); + mHandler = new QnsTimerHandler(); + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_ALARM_TIMER_EXPIRED); + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); + intentFilter.addAction(Intent.ACTION_SCREEN_ON); + intentFilter.addAction(PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED); + intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + mContext.registerReceiver(mBroadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED); + } + + /** + * This method uses AlarmManager to execute the delayed event passed as param. + * + * @param msg message to process. + * @param delayMs timer value for the delay. + * @return unique timer id associated with the registered timer. + */ + int registerTimer(Message msg, long delayMs) { + int timerId = sTimerId.getAndIncrement(); + TimerInfo timerInfo = new TimerInfo(timerId); + timerInfo.setMessage(msg); + timerInfo.setExpireAtElapsedMillis(getSystemElapsedRealTime() + delayMs); + logd("register timer for timerId=" + timerId + ", with delay=" + delayMs); + mHandler.post( + () -> { + mTimerInfos.add(timerInfo); + updateToShortestDelay(mIsAlarmRequired, true); + }); + return timerId; + } + + /** + * This method unregisters the timer associated to given timerId. + * + * @param timerId timer id associated with the running timer. + */ + void unregisterTimer(int timerId) { + if (timerId == INVALID_ID) { + return; + } + logd("unregisterTimer for timerId=" + timerId); + mHandler.post( + () -> { + logd("Cancel timerId=" + timerId); + TimerInfo timerInfo = new TimerInfo(timerId); + if (mTimerInfos.remove(timerInfo) && timerId == mCurrentAlarmTimerId) { + updateToShortestDelay(mIsAlarmRequired, true); + } + }); + } + + /** + * It updates the call state in QnsTimer. If the call is active the minimum timer value for an + * alarm is updated to 0ms. Otherwise the value will be based on device state (Idle, Light Idle + * or Screen off). + * + * @param type Call type {@code @QnsConstants.QnsCallType} + */ + void updateCallState(@QnsConstants.QnsCallType int type) { + if (mCallType == CALL_TYPE_IDLE && type != CALL_TYPE_IDLE) { + mHandler.post( + () -> { + mMinAlarmTimeMs = 0; + if (mIsAlarmRequired) { + updateToShortestDelay(true, false); + } + }); + } + mCallType = type; + if (mCallType == CALL_TYPE_IDLE && mIsAlarmRequired) { + if (mPowerManager.isDeviceIdleMode()) { + mMinAlarmTimeMs = 60000; + } else if (mPowerManager.isDeviceLightIdleMode()) { + mMinAlarmTimeMs = 30000; + } else { + mMinAlarmTimeMs = 10000; // SCREEN_OFF case + } + } + } + + /** + * This method performs the following actions: 1. checks if the shortest timer is set for + * handler or alarm. If not it overrides the earlier set timer with the shortest one. 2. checks + * for timers in the list those have passed the current elapsed time; and notifies them to + * respective handlers. + * + * @param isAlarmRequired flag indicates if timer is need to setup with Alarm. + * @param skipTimerUpdate flag indicates if current scheduled alarm timers needs any change. + * This flag will be false when call type changes or device moves or come out of idle state + * because such cases mandates timer update. + */ + private void updateToShortestDelay(boolean isAlarmRequired, boolean skipTimerUpdate) { + TimerInfo timerInfo = mTimerInfos.peek(); + long elapsedTime = getSystemElapsedRealTime(); + while (timerInfo != null && timerInfo.getExpireAtElapsedMillis() <= elapsedTime) { + logd("Notify timerInfo=" + timerInfo); + timerInfo.getMessage().sendToTarget(); + mTimerInfos.poll(); + timerInfo = mTimerInfos.peek(); + } + if (timerInfo == null) { + logd("No timers are pending to run"); + clearAllTimers(); + return; + } + long delay = timerInfo.getExpireAtElapsedMillis() - elapsedTime; + // Delayed Handler will always set for shortest delay. + if (timerInfo.getTimerId() != mCurrentHandlerTimerId) { + mHandler.removeMessages(EVENT_QNS_TIMER_EXPIRED); + mHandler.sendEmptyMessageDelayed(EVENT_QNS_TIMER_EXPIRED, delay); + mCurrentHandlerTimerId = timerInfo.getTimerId(); + } + + // Alarm will always set for shortest from Math.max(delay, mMinAlarmTimeMs) + if (timerInfo.getTimerId() != mCurrentAlarmTimerId || !skipTimerUpdate) { + if (isAlarmRequired) { + delay = Math.max(delay, mMinAlarmTimeMs); + // check if smaller timer alarm is already running for active timer info. + if (mTimerInfos.contains(new TimerInfo(mCurrentAlarmTimerId)) + && mLastAlarmTriggerAtMs - elapsedTime < delay + && mPendingIntent != null) { + logd( + "Skip update since minimum Alarm Timer already running for timerId=" + + mCurrentAlarmTimerId); + return; + } + logd("Setup alarm for delay " + delay); + mLastAlarmTriggerAtMs = elapsedTime + delay; + setupAlarmFor(mLastAlarmTriggerAtMs); + } else if (mPendingIntent != null) { + mAlarmManager.cancel(mPendingIntent); + mPendingIntent = null; + } + mCurrentAlarmTimerId = timerInfo.getTimerId(); + logd("Update timer to timer id=" + mCurrentAlarmTimerId); + } + } + + private void clearAllTimers() { + mHandler.removeMessages(EVENT_QNS_TIMER_EXPIRED); + if (mPendingIntent != null) { + logd("Cancel Alarm"); + mAlarmManager.cancel(mPendingIntent); + } + mPendingIntent = null; + } + + private void setupAlarmFor(long triggerAtMillis) { + mPendingIntent = + PendingIntent.getBroadcast( + mContext, + 0, + new Intent(ACTION_ALARM_TIMER_EXPIRED), + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + mAlarmManager.setExactAndAllowWhileIdle( + AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, mPendingIntent); + } + + private class AlarmReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + logd("onReceive action=" + action); + switch (action) { + case ACTION_ALARM_TIMER_EXPIRED: + mHandler.sendEmptyMessage(EVENT_QNS_TIMER_EXPIRED); + break; + case Intent.ACTION_SCREEN_OFF: + mHandler.post( + () -> { + mMinAlarmTimeMs = (mCallType == CALL_TYPE_IDLE) ? 10000 : 0; + if (!mIsAlarmRequired) { + mIsAlarmRequired = true; + updateToShortestDelay(true, false); + } + }); + break; + case Intent.ACTION_SCREEN_ON: + mHandler.post( + () -> { + if (mIsAlarmRequired) { + mIsAlarmRequired = false; + updateToShortestDelay(false, false); + } + }); + break; + case PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED: + mHandler.post( + () -> { + if (mPowerManager.isDeviceLightIdleMode()) { + mMinAlarmTimeMs = (mCallType == CALL_TYPE_IDLE) ? 30000 : 0; + if (!mIsAlarmRequired) { + mIsAlarmRequired = true; + updateToShortestDelay(true, false); + } + } else { + mMinAlarmTimeMs = (mCallType == CALL_TYPE_IDLE) ? 10000 : 0; + } + }); + break; + case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED: + mHandler.post( + () -> { + if (mPowerManager.isDeviceIdleMode()) { + mMinAlarmTimeMs = (mCallType == CALL_TYPE_IDLE) ? 60000 : 0; + if (!mIsAlarmRequired) { + mIsAlarmRequired = true; + updateToShortestDelay(true, false); + } + } else { + mMinAlarmTimeMs = (mCallType == CALL_TYPE_IDLE) ? 10000 : 0; + } + }); + break; + default: + break; + } + } + } + + private class QnsTimerHandler extends Handler { + QnsTimerHandler() { + super(mHandlerThread.getLooper()); + } + + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + logd("handleMessage msg.what=" + msg.what); + switch (msg.what) { + case EVENT_QNS_TIMER_EXPIRED: + logd("Timer expired"); + updateToShortestDelay(mIsAlarmRequired, true); + break; + default: + break; + } + } + } + + static class TimerInfo { + private final int mTimerId; + private long mExpireAtElapsedMillis; + private Message mMsg; + + TimerInfo(int timerId) { + mTimerId = timerId; + } + + public int getTimerId() { + return mTimerId; + } + + public Message getMessage() { + return mMsg; + } + + public void setMessage(Message msg) { + mMsg = msg; + } + + public long getExpireAtElapsedMillis() { + return mExpireAtElapsedMillis; + } + + public void setExpireAtElapsedMillis(long expireAtElapsedMillis) { + mExpireAtElapsedMillis = expireAtElapsedMillis; + } + + /** Timers are equals if they share the same timer id. */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TimerInfo)) return false; + TimerInfo timerInfo = (TimerInfo) o; + return mTimerId == timerInfo.mTimerId; + } + + @Override + public int hashCode() { + return Objects.hash(mTimerId); + } + + @Override + public String toString() { + return "TimerInfo{" + + "mTimerId=" + + mTimerId + + ", mExpireAtElapsedMillis=" + + mExpireAtElapsedMillis + + ", mMsg=" + + mMsg + + '}'; + } + } + + @VisibleForTesting + PriorityQueue<TimerInfo> getTimersInfo() { + return mTimerInfos; + } + + void close() { + logd("Closing QnsTimer"); + mHandlerThread.quitSafely(); + mContext.unregisterReceiver(mBroadcastReceiver); + mTimerInfos.clear(); + clearAllTimers(); + } + + private void logd(String s) { + Log.d(TAG, s); + } + + /** + * Dumps the state of {@link QnsTimer} + * + * @param pw {@link PrintWriter} to write the state of the object. + * @param prefix String to append at start of dumped log. + */ + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "------------------------------"); + pw.println(prefix + "QnsTimer:"); + pw.println( + prefix + + "mIsAlarmRequired=" + + mIsAlarmRequired + + ", mCurrentAlarmTimerId=" + + mCurrentAlarmTimerId + + ", mCurrentHandlerTimerId=" + + mCurrentHandlerTimerId + + ", latest timerId=" + + sTimerId.get() + + ", Current elapsed time=" + + getSystemElapsedRealTime()); + pw.println(prefix + "mTimerInfos=" + mTimerInfos); + pw.println(prefix + "mPendingIntent=" + mPendingIntent); + } +} diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QualifiedNetworksServiceImpl.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QualifiedNetworksServiceImpl.java index 9ac37b1..679098e 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/QualifiedNetworksServiceImpl.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QualifiedNetworksServiceImpl.java @@ -358,14 +358,7 @@ public class QualifiedNetworksServiceImpl extends QualifiedNetworksService { NetworkAvailabilityProviderImpl provider = providerMap.getValue(); provider.dump(pw, " "); } - IwlanNetworkStatusTracker iwlanNst = mQnsComponents.getIwlanNetworkStatusTracker(); - if (iwlanNst != null) { - iwlanNst.dump(pw, " "); - } - WifiQualityMonitor wQM = mQnsComponents.getWifiQualityMonitor(); - if (wQM != null) { - wQM.dump(pw, " "); - } + mQnsComponents.dump(pw); pw.println("=============================="); } } diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/RestrictManager.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/RestrictManager.java index 1c15346..21eaf7c 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/RestrictManager.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/RestrictManager.java @@ -18,6 +18,7 @@ package com.android.telephony.qns; import static com.android.telephony.qns.DataConnectionStatusTracker.STATE_CONNECTED; import static com.android.telephony.qns.DataConnectionStatusTracker.STATE_HANDOVER; +import static com.android.telephony.qns.QnsConstants.INVALID_ID; import android.annotation.IntDef; import android.net.NetworkCapabilities; @@ -161,6 +162,7 @@ class RestrictManager { private QnsCallStatusTracker mQnsCallStatusTracker; private QnsCallStatusTracker.ActiveCallTracker mActiveCallTracker; private QnsImsManager mQnsImsManager; + private QnsTimer mQnsTimer; private WifiBackhaulMonitor mWifiBackhaulMonitor; private QnsMetrics mQnsMetrics; private int mNetCapability; @@ -174,6 +176,7 @@ class RestrictManager { private int mFallbackCounterOnDataConnectionFail; private boolean mIsRttStatusCheckRegistered = false; private int mLastDataConnectionTransportType; + private int mFallbackTimerId = -1; private boolean mIsTimerRunningOnDataConnectionFail = false; private Pair<Integer, Long> mDeferredThrottlingEvent = null; @@ -183,6 +186,7 @@ class RestrictManager { @Annotation.CallState private int mCallState; private Map<Integer, RestrictInfo> mRestrictInfos = new ConcurrentHashMap<>(); + private Map<Restriction, Integer> mRestrictionTimers = new ConcurrentHashMap<>(); private class RestrictManagerHandler extends Handler { RestrictManagerHandler(Looper l) { @@ -239,6 +243,9 @@ class RestrictManager { .getRestrictionMap() .get(restriction.mRestrictType)) { releaseRestriction(transportType, restriction.mRestrictType); + mQnsTimer.unregisterTimer(mRestrictionTimers + .getOrDefault(restriction, INVALID_ID)); + mRestrictionTimers.remove(restriction); } break; @@ -248,6 +255,7 @@ class RestrictManager { "Initial Data Connection fail timer expired" + mIsTimerRunningOnDataConnectionFail); + mQnsTimer.unregisterTimer(mFallbackTimerId); if (mIsTimerRunningOnDataConnectionFail) { int currTransportType = message.arg1; fallbackToOtherTransportOnDataConnectionFail(currTransportType); @@ -452,6 +460,7 @@ class RestrictManager { mTelephonyListener = qnsComponents.getQnsTelephonyListener(mSlotId); mQnsEventDispatcher = qnsComponents.getQnsEventDispatcher(mSlotId); mQnsCarrierConfigManager = qnsComponents.getQnsCarrierConfigManager(mSlotId); + mQnsTimer = qnsComponents.getQnsTimer(); mHandler = new RestrictManagerHandler(loop); mNetCapability = netCapability; mDataConnectionStatusTracker = dcst; @@ -520,6 +529,7 @@ class RestrictManager { if (mNetCapability == NetworkCapabilities.NET_CAPABILITY_IMS) { mQnsImsManager.unregisterImsRegistrationStatusChanged(mHandler); } + mRestrictionTimers.clear(); } private void onWfcModeChanged(int prefMode, @QnsConstants.CellularCoverage int coverage) { @@ -778,9 +788,7 @@ class RestrictManager { mIsTimerRunningOnDataConnectionFail = false; mRetryCounterOnDataConnectionFail = 0; - if (mHandler.hasMessages(EVENT_INITIAL_DATA_CONNECTION_FAIL_RETRY_TIMER_EXPIRED)) { - mHandler.removeMessages(EVENT_INITIAL_DATA_CONNECTION_FAIL_RETRY_TIMER_EXPIRED); - } + mQnsTimer.unregisterTimer(mFallbackTimerId); } private void processDataConnectionDisconnected() { @@ -918,7 +926,7 @@ class RestrictManager { transportType, 0, null); - mHandler.sendMessageDelayed(msg, (long) fallbackRetryTimer); + mFallbackTimerId = mQnsTimer.registerTimer(msg, fallbackRetryTimer); mIsTimerRunningOnDataConnectionFail = true; } } @@ -1293,6 +1301,7 @@ class RestrictManager { removeReleaseRestrictionMessage(restriction); } restrictionMap.remove(restriction.mRestrictType); + mRestrictionTimers.remove(restriction); needNotify = true; } if (needNotify && !skipNotify) { @@ -1329,7 +1338,8 @@ class RestrictManager { Message msg = mHandler.obtainMessage(EVENT_RELEASE_RESTRICTION, transportType, 0, restriction); long delayInMillis = restriction.mReleaseTime - SystemClock.elapsedRealtime(); - mHandler.sendMessageDelayed(msg, delayInMillis); + int timerId = mQnsTimer.registerTimer(msg, delayInMillis); + mRestrictionTimers.put(restriction, timerId); Log.d( mLogTag, restrictTypeToString(restriction.mRestrictType) @@ -1343,7 +1353,8 @@ class RestrictManager { Log.e(mLogTag, "removeReleaseRestrictionMessage restriction is null"); return; } - mHandler.removeMessages(EVENT_RELEASE_RESTRICTION, restriction); + mQnsTimer.unregisterTimer(mRestrictionTimers.getOrDefault(restriction, INVALID_ID)); + mRestrictionTimers.remove(restriction); } void registerRestrictInfoChanged(Handler h, int what) { diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/WifiBackhaulMonitor.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/WifiBackhaulMonitor.java index be3c22c..6bef150 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/WifiBackhaulMonitor.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/WifiBackhaulMonitor.java @@ -16,6 +16,8 @@ package com.android.telephony.qns; +import static com.android.telephony.qns.QnsConstants.INVALID_ID; + import android.content.Context; import android.net.ConnectivityManager; import android.net.LinkProperties; @@ -28,6 +30,8 @@ import android.os.Message; import android.telephony.AccessNetworkConstants; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -56,6 +60,7 @@ class WifiBackhaulMonitor { private final HandlerThread mHandlerThread; private final Handler mHandler; private final QnsCarrierConfigManager mConfigManager; + private final QnsTimer mQnsTimer; private boolean mRttResult = false; ArrayList<InetAddress> mValidIpList = new ArrayList<>(); @@ -65,6 +70,7 @@ class WifiBackhaulMonitor { private boolean mIsIwlanConnected = false; private boolean mIsRttRunning = false; private String mInterfaceName = null; + private int mRttTimerId = INVALID_ID; private class BackhaulHandler extends Handler { BackhaulHandler() { @@ -118,6 +124,7 @@ class WifiBackhaulMonitor { Context context, QnsCarrierConfigManager configManager, QnsImsManager imsManager, + QnsTimer qnstimer, int slotIndex) { mSlotIndex = slotIndex; mTag = WifiBackhaulMonitor.class.getSimpleName() + "[" + mSlotIndex + "]"; @@ -125,6 +132,7 @@ class WifiBackhaulMonitor { mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); mConfigManager = configManager; mQnsImsManager = imsManager; + mQnsTimer = qnstimer; mNetworkCallback = new WiFiStatusCallback(); mRegistrantList = new QnsRegistrantList(); mHandlerThread = new HandlerThread(mTag); @@ -174,8 +182,9 @@ class WifiBackhaulMonitor { /** Triggers the request to check RTT. */ void requestRttCheck() { if (!mIsRttRunning) { - if (mHandler.hasMessages(EVENT_START_RTT_CHECK)) { - mHandler.removeMessages(EVENT_START_RTT_CHECK); + if (mRttTimerId != INVALID_ID) { + mQnsTimer.unregisterTimer(mRttTimerId); + mRttTimerId = INVALID_ID; } mHandler.sendEmptyMessage(EVENT_START_RTT_CHECK); } else { @@ -232,13 +241,17 @@ class WifiBackhaulMonitor { } private void startRttSchedule(int delay) { - mHandler.sendEmptyMessageDelayed(EVENT_START_RTT_CHECK, delay); + log("start RTT schedule for " + delay); + mRttTimerId = mQnsTimer.registerTimer(Message.obtain(mHandler, EVENT_START_RTT_CHECK), + delay); mIsRttScheduled = true; } private void stopRttSchedule() { if (mIsRttScheduled) { - mHandler.removeMessages(EVENT_START_RTT_CHECK); + log("stop RTT schedule"); + mQnsTimer.unregisterTimer(mRttTimerId); + mRttTimerId = INVALID_ID; mIsRttScheduled = false; } } @@ -350,6 +363,11 @@ class WifiBackhaulMonitor { mIsRttScheduled = false; } + @VisibleForTesting + int getRttTimerId() { + return mRttTimerId; + } + private void log(String s) { Log.d(mTag, s); } diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/WifiQualityMonitor.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/WifiQualityMonitor.java index bf758a9..0c5ea2a 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/WifiQualityMonitor.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/WifiQualityMonitor.java @@ -53,6 +53,8 @@ public class WifiQualityMonitor extends QualityMonitor { private final ConnectivityManager mConnectivityManager; private final WiFiThresholdCallback mWiFiThresholdCallback; private final NetworkRequest.Builder mBuilder; + private final QnsTimer mQnsTimer; + private final List<Integer> mTimerIds; private int mWifiRssi; @VisibleForTesting Handler mHandler; @@ -60,6 +62,7 @@ public class WifiQualityMonitor extends QualityMonitor { private static final int BACKHAUL_TIMER_DEFAULT = 3000; static final int INVALID_RSSI = -127; private boolean mIsRegistered = false; + private boolean mIsBackhaulRunning; private class WiFiThresholdCallback extends ConnectivityManager.NetworkCallback { /** Callback Received based on meeting Wifi RSSI Threshold Registered or Wifi Lost */ @@ -94,11 +97,7 @@ public class WifiQualityMonitor extends QualityMonitor { synchronized void validateWqmStatus(int wifiRssi) { if (isWifiRssiValid(wifiRssi)) { Log.d(mTag, "Registered Threshold @ Wqm Status check =" + mRegisteredThreshold); - if (!mHandler.hasMessages(EVENT_WIFI_NOTIFY_TIMER_EXPIRED)) { - mHandler.obtainMessage(EVENT_WIFI_RSSI_CHANGED, wifiRssi, 0).sendToTarget(); - } else { - Log.d(mTag, "BackhaulCheck in Progress , skip validation"); - } + mHandler.obtainMessage(EVENT_WIFI_RSSI_CHANGED, wifiRssi, 0).sendToTarget(); } else { Log.d(mTag, "Cancel backhaul if running for invalid SS received"); clearBackHaulTimer(); @@ -117,21 +116,24 @@ public class WifiQualityMonitor extends QualityMonitor { } private void clearBackHaulTimer() { - if (mHandler.hasMessages(EVENT_WIFI_NOTIFY_TIMER_EXPIRED)) { - Log.d(mTag, "Stop all active backhaul timers"); - mHandler.removeMessages(EVENT_WIFI_NOTIFY_TIMER_EXPIRED); - mWaitingThresholds.clear(); + Log.d(mTag, "Stop all active backhaul timers"); + for (int timerId : mTimerIds) { + mQnsTimer.unregisterTimer(timerId); } + mTimerIds.clear(); + mWaitingThresholds.clear(); } /** * Create WifiQualityMonitor object for accessing WifiManager, ConnectivityManager to monitor * RSSI, build parameters for registering threshold & callback listening. */ - WifiQualityMonitor(Context context) { + WifiQualityMonitor(Context context, QnsTimer qnsTimer) { super(QualityMonitor.class.getSimpleName() + "-I"); mTag = WifiQualityMonitor.class.getSimpleName() + "-I"; mContext = context; + mQnsTimer = qnsTimer; + mTimerIds = new ArrayList<>(); HandlerThread handlerThread = new HandlerThread(mTag); handlerThread.start(); mHandler = new WiFiEventsHandler(handlerThread.getLooper()); @@ -239,9 +241,7 @@ public class WifiQualityMonitor extends QualityMonitor { } private void validateForWifiBackhaul(int wifiRssi) { - if (mHandler.hasMessages(EVENT_WIFI_NOTIFY_TIMER_EXPIRED)) { - mHandler.removeMessages(EVENT_WIFI_NOTIFY_TIMER_EXPIRED); - } + mIsBackhaulRunning = false; for (Map.Entry<String, List<Threshold>> entry : mThresholdsList.entrySet()) { if (mWaitingThresholds.getOrDefault(entry.getKey(), false)) { continue; @@ -262,9 +262,13 @@ public class WifiQualityMonitor extends QualityMonitor { } if (backhaul > 0) { mWaitingThresholds.put(key, true); - if (!mHandler.hasMessages(EVENT_WIFI_NOTIFY_TIMER_EXPIRED)) { - Log.d(mTag, "Starting backhaul timer = " + backhaul); - mHandler.sendEmptyMessageDelayed(EVENT_WIFI_NOTIFY_TIMER_EXPIRED, backhaul); + Log.d(mTag, "Starting backhaul timer = " + backhaul); + if (!mIsBackhaulRunning) { + mTimerIds.add( + mQnsTimer.registerTimer( + Message.obtain(mHandler, EVENT_WIFI_NOTIFY_TIMER_EXPIRED), + backhaul)); + mIsBackhaulRunning = true; } } else { Log.d(mTag, "Notify for RSSI Threshold Registered w/o Backhaul = " + backhaul); @@ -302,7 +306,6 @@ public class WifiQualityMonitor extends QualityMonitor { unregisterCallback(); if (mThresholdsList.isEmpty()) { clearBackHaulTimer(); - mWaitingThresholds.clear(); } } else { Log.d(mTag, "Listening to threshold = " + mRegisteredThreshold); @@ -378,8 +381,8 @@ public class WifiQualityMonitor extends QualityMonitor { prefix + ", mIsRegistered=" + mIsRegistered - + ", backhaulstatus =" - + mHandler.hasMessages(EVENT_WIFI_NOTIFY_TIMER_EXPIRED)); + + ", mIsBackhaulRunning=" + + mIsBackhaulRunning); pw.println( prefix + "mWifiRssi=" diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCallStatusTrackerTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCallStatusTrackerTest.java index 9dea914..5d7f405 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCallStatusTrackerTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCallStatusTrackerTest.java @@ -24,6 +24,10 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; @@ -53,8 +57,10 @@ import org.junit.runners.JUnit4; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; +import org.mockito.stubbing.Answer; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; @RunWith(JUnit4.class) @@ -69,6 +75,8 @@ public class QnsCallStatusTrackerTest extends QnsTest { private Handler mLowQualityHandler; private MockitoSession mMockSession; List<CallState> mTestCallStateList = new ArrayList<>(); + int mId = 0; + HashMap<Integer, Message> mMessageHashMap = new HashMap<>(); @Before public void setUp() throws Exception { @@ -82,8 +90,27 @@ public class QnsCallStatusTrackerTest extends QnsTest { mImsHandler = new Handler(mTestLooperListener.getLooper()); mEmergencyHandler = new Handler(mTestLooperListener.getLooper()); mLowQualityHandler = new Handler(mLowQualityListenerLooper.getLooper()); + mMessageHashMap = new HashMap<>(); + when(mMockQnsTimer.registerTimer(isA(Message.class), anyLong())).thenAnswer( + (Answer<Integer>) invocation -> { + Message msg = (Message) invocation.getArguments()[0]; + long delay = (long) invocation.getArguments()[1]; + msg.getTarget().sendMessageDelayed(msg, delay); + mMessageHashMap.put(++mId, msg); + return mId; + }); + + doAnswer(invocation -> { + int timerId = (int) invocation.getArguments()[0]; + Message msg = mMessageHashMap.get(timerId); + if (msg != null && msg.getTarget() != null) { + msg.getTarget().removeMessages(msg.what, msg.obj); + } + return null; + }).when(mMockQnsTimer).unregisterTimer(anyInt()); mCallTracker = new QnsCallStatusTracker( - mMockQnsTelephonyListener, mMockQnsConfigManager, 0, mTestLooper.getLooper()); + mMockQnsTelephonyListener, mMockQnsConfigManager, mMockQnsTimer, 0, + mTestLooper.getLooper()); mCallTracker.registerCallTypeChangedListener( NetworkCapabilities.NET_CAPABILITY_IMS, mImsHandler, 1, null); mCallTracker.registerCallTypeChangedListener( diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsComponentsTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsComponentsTest.java index 8d88d75..73a5249 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsComponentsTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsComponentsTest.java @@ -51,6 +51,7 @@ public class QnsComponentsTest extends QnsTest { assertNull(qnsComponents.getWifiBackhaulMonitor(slotId)); assertNull(qnsComponents.getWifiQualityMonitor()); assertNull(qnsComponents.getIwlanNetworkStatusTracker()); + assertNull(qnsComponents.getQnsTimer()); qnsComponents.createQnsComponents(slotId); @@ -65,6 +66,7 @@ public class QnsComponentsTest extends QnsTest { assertNotNull(qnsComponents.getWifiBackhaulMonitor(slotId)); assertNotNull(qnsComponents.getWifiQualityMonitor()); assertNotNull(qnsComponents.getIwlanNetworkStatusTracker()); + assertNotNull(qnsComponents.getQnsTimer()); } @@ -82,6 +84,7 @@ public class QnsComponentsTest extends QnsTest { mMockQnsProvisioningListener, mMockQnsTelephonyListener, mMockQnsCallStatusTracker, + mMockQnsTimer, mMockWifiBm, mMockWifiQm, mMockQnsMetrics, @@ -98,6 +101,7 @@ public class QnsComponentsTest extends QnsTest { assertNotNull(qnsComponents.getWifiBackhaulMonitor(slotId)); assertNotNull(qnsComponents.getWifiQualityMonitor()); assertNotNull(qnsComponents.getIwlanNetworkStatusTracker()); + assertNotNull(qnsComponents.getQnsTimer()); assertNotNull(qnsComponents.getQnsMetrics()); qnsComponents.closeComponents(slotId); @@ -113,6 +117,7 @@ public class QnsComponentsTest extends QnsTest { assertNull(qnsComponents.getWifiBackhaulMonitor(slotId)); assertNull(qnsComponents.getWifiQualityMonitor()); assertNull(qnsComponents.getIwlanNetworkStatusTracker()); + assertNull(qnsComponents.getQnsTimer()); assertNull(qnsComponents.getQnsMetrics()); verify(mMockQnsTelephonyListener).close(); @@ -126,6 +131,7 @@ public class QnsComponentsTest extends QnsTest { verify(mMockWifiBm).close(); verify(mMockWifiQm).close(); verify(mMockIwlanNetworkStatusTracker).close(); + verify(mMockQnsTimer).close(); verify(mMockQnsMetrics).close(); } } diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTest.java index c973a25..0dff376 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTest.java @@ -32,6 +32,7 @@ import android.net.ConnectivityManager; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Handler; +import android.os.PowerManager; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -73,6 +74,7 @@ public abstract class QnsTest { @Mock protected WifiQualityMonitor mMockWifiQm; @Mock protected CellularNetworkStatusTracker mMockCellNetStatusTracker; @Mock protected CellularQualityMonitor mMockCellularQm; + @Mock protected PowerManager mMockPowerManager; @Mock protected QnsImsManager mMockQnsImsManager; @Mock protected QnsCarrierConfigManager mMockQnsConfigManager; @Mock protected QnsEventDispatcher mMockQnsEventDispatcher; @@ -80,6 +82,7 @@ public abstract class QnsTest { @Mock protected QnsTelephonyListener mMockQnsTelephonyListener; @Mock protected QnsCallStatusTracker mMockQnsCallStatusTracker; @Mock protected WifiBackhaulMonitor mMockWifiBm; + @Mock protected QnsTimer mMockQnsTimer; @Mock protected QnsMetrics mMockQnsMetrics; protected QnsComponents[] mQnsComponents = new QnsComponents[2]; @@ -109,6 +112,7 @@ public abstract class QnsTest { mMockQnsProvisioningListener, mMockQnsTelephonyListener, mMockQnsCallStatusTracker, + mMockQnsTimer, mMockWifiBm, mMockWifiQm, mMockQnsMetrics, @@ -126,6 +130,7 @@ public abstract class QnsTest { mMockQnsProvisioningListener, mMockQnsTelephonyListener, mMockQnsCallStatusTracker, + mMockQnsTimer, mMockWifiBm, mMockWifiQm, mMockQnsMetrics, @@ -144,7 +149,7 @@ public abstract class QnsTest { when(sMockContext.getSystemService(ImsManager.class)).thenReturn(mMockImsManager); when(sMockContext.getSystemService(WifiManager.class)).thenReturn(mMockWifiManager); when(sMockContext.getSystemService(CountryDetector.class)).thenReturn(mMockCountryDetector); - + when(sMockContext.getSystemService(PowerManager.class)).thenReturn(mMockPowerManager); when(sMockContext.getResources()).thenReturn(mMockResources); } @@ -165,6 +170,7 @@ public abstract class QnsTest { when(mMockCountryDetector.detectCountry()) .thenReturn(new Country("US", Country.COUNTRY_SOURCE_LOCATION)); + when(mMockPowerManager.isDeviceIdleMode()).thenReturn(false); } private void stubOthers() { diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTimerTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTimerTest.java new file mode 100644 index 0000000..c4acd5a --- /dev/null +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTimerTest.java @@ -0,0 +1,309 @@ +/* + * 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.telephony.qns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.Message; +import android.os.PowerManager; +import android.os.SystemClock; +import android.os.test.TestLooper; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class QnsTimerTest extends QnsTest { + + private static final int EVENT_QNS_TIMER_EXPIRED = 1; + static final String ACTION_ALARM_TIMER_EXPIRED = + "com.android.telephony.qns.action.ALARM_TIMER_EXPIRED"; + static final String KEY_TIMER_ID = "key_timer_id"; + @Mock private Context mContext; + @Mock private AlarmManager mAlarmManager; + @Mock private PowerManager mPowerManager; + @Mock Message mMessage; + private QnsTimer mQnsTimer; + private BroadcastReceiver mBroadcastReceiver; + int mTimerId; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mContext = spy(ApplicationProvider.getApplicationContext()); + when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager); + when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager); + mQnsTimer = new QnsTimer(mContext); + ArgumentCaptor<BroadcastReceiver> args = ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext).registerReceiver(args.capture(), isA(IntentFilter.class), anyInt()); + mBroadcastReceiver = args.getValue(); + } + + @After + public void tearDown() { + mQnsTimer.close(); + } + + @Test + public void testRegisterTimerForScreenOff() { + mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + + mTimerId = mQnsTimer.registerTimer(mMessage, 30000); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + + assertTrue(mQnsTimer.getTimersInfo().contains(new QnsTimer.TimerInfo(mTimerId))); + assertTrue(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED)); + verify(mAlarmManager) + .setExactAndAllowWhileIdle(anyInt(), anyLong(), isA(PendingIntent.class)); + } + + @Test + public void testRegisterTimerForScreenOn() { + mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_ON)); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + + mTimerId = mQnsTimer.registerTimer(mMessage, 80000); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + + assertTrue(mQnsTimer.getTimersInfo().contains(new QnsTimer.TimerInfo(mTimerId))); + assertTrue(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED)); + verify(mAlarmManager, never()) + .setExactAndAllowWhileIdle(anyInt(), anyLong(), isA(PendingIntent.class)); + } + + @Test + public void testUnregisterForInvalidId() { + testRegisterTimerForScreenOn(); + int timerInfoSize = mQnsTimer.getTimersInfo().size(); + mQnsTimer.unregisterTimer(QnsConstants.INVALID_ID); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + assertEquals(timerInfoSize, mQnsTimer.getTimersInfo().size()); + } + + @Test + public void testUnregisterTimerForScreenOff() { + testRegisterTimerForScreenOff(); + mQnsTimer.unregisterTimer(mTimerId); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + assertFalse(mQnsTimer.getTimersInfo().contains(new QnsTimer.TimerInfo(mTimerId))); + assertFalse(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED)); + verify(mAlarmManager).cancel(isA(PendingIntent.class)); + } + + @Test + public void testUnregisterTimerForScreenOn() { + testRegisterTimerForScreenOn(); + mQnsTimer.unregisterTimer(mTimerId); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + assertFalse(mQnsTimer.getTimersInfo().contains(new QnsTimer.TimerInfo(mTimerId))); + assertFalse(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED)); + verify(mAlarmManager, never()).cancel(isA(PendingIntent.class)); + } + + @Test + public void testUpdateTimerTypeToAlarm() { + testRegisterTimerForScreenOn(); + + mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + + verify(mAlarmManager) + .setExactAndAllowWhileIdle(anyInt(), anyLong(), isA(PendingIntent.class)); + } + + @Test + public void testTimerExpired() { + mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + + mTimerId = mQnsTimer.registerTimer(mMessage, 50); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + + assertTrue(mQnsTimer.getTimersInfo().contains(new QnsTimer.TimerInfo(mTimerId))); + assertTrue(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED)); + verify(mAlarmManager) + .setExactAndAllowWhileIdle(anyInt(), anyLong(), isA(PendingIntent.class)); + + waitForDelayedHandlerAction(mQnsTimer.mHandler, 40, 200); + mBroadcastReceiver.onReceive(mContext, new Intent(ACTION_ALARM_TIMER_EXPIRED)); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + + verify(mMessage).sendToTarget(); + assertFalse(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED)); + verify(mAlarmManager).cancel(isA(PendingIntent.class)); + } + + @Test + public void testMultipleTimerRegistered() { + mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + TestLooper testLooper = new TestLooper(); + Handler h = new Handler(testLooper.getLooper()); + + mQnsTimer.registerTimer(Message.obtain(h, 4), 300); + mQnsTimer.registerTimer(Message.obtain(h, 3), 200); + mQnsTimer.registerTimer(Message.obtain(h, 1), 50); + mQnsTimer.registerTimer(Message.obtain(h, 1), 50); + mQnsTimer.registerTimer(Message.obtain(h, 2), 100); + mQnsTimer.registerTimer(Message.obtain(h, 2), 100); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 320, 100); + + // alarm timer should update for shortest delay and since the minimum timer value is 10 + // secs for screen off condition, the alarm timer will not replace be replaced until new + // timer requested for less than 10 secs. + verify(mAlarmManager) + .setExactAndAllowWhileIdle(anyInt(), anyLong(), isA(PendingIntent.class)); + + // verify order of message received: + Message msg = testLooper.nextMessage(); + assertEquals(1, msg.what); + msg = testLooper.nextMessage(); + assertEquals(1, msg.what); + msg = testLooper.nextMessage(); + assertEquals(2, msg.what); + msg = testLooper.nextMessage(); + assertEquals(2, msg.what); + msg = testLooper.nextMessage(); + assertEquals(3, msg.what); + msg = testLooper.nextMessage(); + assertEquals(4, msg.what); + } + + @Test + public void testCancelOngoingAlarm() { + mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + TestLooper testLooper = new TestLooper(); + Handler h = new Handler(testLooper.getLooper()); + + int timerId1 = mQnsTimer.registerTimer(Message.obtain(h, 1), 61 * 1000); + int timerId2 = mQnsTimer.registerTimer(Message.obtain(h, 2), 1000); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + + assertEquals(2, mQnsTimer.getTimersInfo().size()); + assertEquals(timerId2, mQnsTimer.getTimersInfo().peek().getTimerId()); + assertTrue(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED)); + verify(mAlarmManager, times(2)) + .setExactAndAllowWhileIdle(anyInt(), anyLong(), isA(PendingIntent.class)); + + mQnsTimer.unregisterTimer(timerId2); + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + assertEquals(1, mQnsTimer.getTimersInfo().size()); + assertEquals(timerId1, mQnsTimer.getTimersInfo().peek().getTimerId()); + assertTrue(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED)); + + verify(mAlarmManager, times(3)) + .setExactAndAllowWhileIdle(anyInt(), anyLong(), isA(PendingIntent.class)); + } + + @Test + public void testAlarmOnLiteIdleModeMinDelay() { + int setDelay = 20000; + when(mPowerManager.isDeviceLightIdleMode()).thenReturn(true); + mBroadcastReceiver.onReceive( + sMockContext, new Intent(PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED)); + long delay = setupAlarmForDelay(setDelay); + + // assume 100ms as max delay in execution + assertTrue(delay < 30000 && delay > 30000 - 100); + } + + @Test + public void testAlarmOnLiteIdleMode() { + int setDelay = 40000; + when(mPowerManager.isDeviceLightIdleMode()).thenReturn(true); + mBroadcastReceiver.onReceive( + sMockContext, new Intent(PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED)); + long delay = setupAlarmForDelay(setDelay); + + // assume 100ms as max delay in execution + assertTrue(delay < setDelay && delay > setDelay - 100); + } + + @Test + public void testAlarmOnIdleModeMinDelay() { + int setDelay = 50000; + when(mPowerManager.isDeviceIdleMode()).thenReturn(true); + mBroadcastReceiver.onReceive( + sMockContext, new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)); + long delay = setupAlarmForDelay(setDelay); + + // assume 100ms as max delay in execution + assertTrue(delay < 60000 && delay > 60000 - 100); + } + + @Test + public void testAlarmOnIdleMode() { + int setDelay = 70000; + when(mPowerManager.isDeviceIdleMode()).thenReturn(true); + mBroadcastReceiver.onReceive( + sMockContext, new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)); + long delay = setupAlarmForDelay(setDelay); + + // assume 100ms as max delay in execution + assertTrue(delay < setDelay && delay > setDelay - 100); + } + + private long setupAlarmForDelay(int setDelay) { + mQnsTimer.registerTimer(mMessage, setDelay); + + waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200); + ArgumentCaptor<Long> capture = ArgumentCaptor.forClass(Long.class); + verify(mAlarmManager) + .setExactAndAllowWhileIdle(anyInt(), capture.capture(), isA(PendingIntent.class)); + return capture.getValue() - SystemClock.elapsedRealtime(); + } + + @Test + public void testAlarmInCallActiveState() { + mQnsTimer.updateCallState(QnsConstants.CALL_TYPE_VOICE); + int setDelay = 4000; + when(mPowerManager.isDeviceIdleMode()).thenReturn(true); + mBroadcastReceiver.onReceive( + sMockContext, new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)); + long delay = setupAlarmForDelay(setDelay); + + // assume 100ms as max delay in execution + assertTrue(delay < setDelay && delay > setDelay - 100); + } +} diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QualifiedNetworksServiceImplTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QualifiedNetworksServiceImplTest.java index 87eb761..9a1d559 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QualifiedNetworksServiceImplTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QualifiedNetworksServiceImplTest.java @@ -90,6 +90,7 @@ public class QualifiedNetworksServiceImplTest extends QnsTest { mMockQnsProvisioningListener, mMockQnsTelephonyListener, mMockQnsCallStatusTracker, + mMockQnsTimer, mMockWifiBm, mMockWifiQm, mMockQnsMetrics, diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/RestrictManagerTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/RestrictManagerTest.java index f897366..ddc193a 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/RestrictManagerTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/RestrictManagerTest.java @@ -46,7 +46,10 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; @@ -74,7 +77,9 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; +import org.mockito.stubbing.Answer; +import java.util.HashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -93,6 +98,8 @@ public class RestrictManagerTest extends QnsTest { private QnsImsManager mQnsImsManager; protected TestLooper mTestLooper; + int mId = 0; + HashMap<Integer, Message> mMessageHashMap = new HashMap<>(); HandlerThread mHandlerThread = new HandlerThread("") { @@ -123,6 +130,24 @@ public class RestrictManagerTest extends QnsTest { when(mMockQnsConfigManager.getWaitingTimerForPreferredTransportOnPowerOn( AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) .thenReturn(0); + when(mMockQnsTimer.registerTimer(isA(Message.class), anyLong())).thenAnswer( + (Answer<Integer>) invocation -> { + Message msg = (Message) invocation.getArguments()[0]; + long delay = (long) invocation.getArguments()[1]; + msg.getTarget().sendMessageDelayed(msg, delay); + mMessageHashMap.put(++mId, msg); + return mId; + }); + + doAnswer(invocation -> { + int timerId = (int) invocation.getArguments()[0]; + Message msg = mMessageHashMap.get(timerId); + if (msg != null && msg.getTarget() != null) { + msg.getTarget().removeMessages(msg.what, msg.obj); + } + return null; + }).when(mMockQnsTimer).unregisterTimer(anyInt()); + mTestLooper = new TestLooper(); mHandlerThread.start(); @@ -140,6 +165,7 @@ public class RestrictManagerTest extends QnsTest { mMockQnsProvisioningListener, mTelephonyListener, mMockQnsCallStatusTracker, + mMockQnsTimer, mMockWifiBm, mMockWifiQm, mMockQnsMetrics, diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/WifiBackhaulMonitorTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/WifiBackhaulMonitorTest.java index 3f81fb1..b05d3c1 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/WifiBackhaulMonitorTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/WifiBackhaulMonitorTest.java @@ -17,9 +17,11 @@ package com.android.telephony.qns; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.telephony.qns.QnsConstants.INVALID_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.anyInt; @@ -76,6 +78,7 @@ public class WifiBackhaulMonitorTest extends QnsTest { private StaticMockitoSession mMockitoSession; private LinkProperties mLinkProperties = new LinkProperties(); private String mServerAddress; + private QnsTimer mQnsTimer; private int[] mRttConfigs; HandlerThread mHt = @@ -85,7 +88,11 @@ public class WifiBackhaulMonitorTest extends QnsTest { super.onLooperPrepared(); mWbm = new WifiBackhaulMonitor( - sMockContext, mMockQnsConfigManager, mMockQnsImsManager, 0); + sMockContext, + mMockQnsConfigManager, + mMockQnsImsManager, + mQnsTimer, + 0); setReady(true); } }; @@ -121,6 +128,7 @@ public class WifiBackhaulMonitorTest extends QnsTest { mRttConfigs = null; mLinkProperties.setInterfaceName("iwlan0"); mLatch = new CountDownLatch(1); + mQnsTimer = new QnsTimer(sMockContext); mMockitoSession = mockitoSession() @@ -282,7 +290,7 @@ public class WifiBackhaulMonitorTest extends QnsTest { null)) .sendToTarget(); waitForDelayedHandlerAction(mRttHandler, 100, 100); - assertTrue(mRttHandler.hasMessages(EVENT_START_RTT_CHECK)); + assertNotEquals(mWbm.getRttTimerId(), INVALID_ID); } @Test diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/WifiQualityMonitorTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/WifiQualityMonitorTest.java index 75d8db7..8e51d2c 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/WifiQualityMonitorTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/WifiQualityMonitorTest.java @@ -53,8 +53,10 @@ import java.util.concurrent.Executor; @RunWith(JUnit4.class) public class WifiQualityMonitorTest extends QnsTest { + private static final int EVENT_QNS_TIMER_EXPIRED = 1; Context mContext; @Mock ConnectivityManager mConnectivityManager; + QnsTimer mQnsTimer; @Mock WifiManager mWifiManager; @Mock NetworkCapabilities mNetworkCapabilityManager; @Mock private Network mMockNetwork; @@ -96,7 +98,8 @@ public class WifiQualityMonitorTest extends QnsTest { mWifiInfo = new WifiInfo.Builder().setRssi(mSetRssi).build(); mLatch = new CountDownLatch(1); mThresholdListener = new ThresholdListener(mExecutor); - mWifiQualityMonitor = new WifiQualityMonitor(mContext); + mQnsTimer = new QnsTimer(mContext); + mWifiQualityMonitor = new WifiQualityMonitor(mContext, mQnsTimer); } @Test @@ -232,7 +235,7 @@ public class WifiQualityMonitorTest extends QnsTest { } @Test - public void testBackhaulTimer() throws InterruptedException { + public void testBackhaulTimer() { mSetRssi = -65; mLatch = new CountDownLatch(1); mWifiInfo = new WifiInfo.Builder().setRssi(mSetRssi).build(); @@ -277,8 +280,9 @@ public class WifiQualityMonitorTest extends QnsTest { mWifiQualityMonitor.mHandler.obtainMessage(EVENT_WIFI_RSSI_CHANGED, -65, 0).sendToTarget(); waitForDelayedHandlerAction(mWifiQualityMonitor.mHandler, 1000, 200); - assertTrue(mWifiQualityMonitor.mHandler.hasMessages(EVENT_WIFI_NOTIFY_TIMER_EXPIRED)); + assertTrue(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED)); waitForDelayedHandlerAction(mWifiQualityMonitor.mHandler, 4000, 200); + assertFalse(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED)); assertFalse(mWifiQualityMonitor.mHandler.hasMessages(EVENT_WIFI_NOTIFY_TIMER_EXPIRED)); } @@ -329,23 +333,6 @@ public class WifiQualityMonitorTest extends QnsTest { isWifiRssiChangedHandlerNotPosted(); } - @Test - public void testSkipValidateWqmStatus_WithBackhaulInProgress() { - mSetRssi = -65; - mLatch = new CountDownLatch(1); - mWifiInfo = new WifiInfo.Builder().setRssi(mSetRssi).build(); - when(mWifiManager.getConnectionInfo()).thenReturn(mWifiInfo); - - setWqmThreshold(); - mWifiQualityMonitor.validateWqmStatus(-65); - - waitForDelayedHandlerAction(mWifiQualityMonitor.mHandler, 1000, 200); - assertTrue(mWifiQualityMonitor.mHandler.hasMessages(EVENT_WIFI_NOTIFY_TIMER_EXPIRED)); - - mWifiQualityMonitor.validateWqmStatus(-68); - assertFalse(mWifiQualityMonitor.mHandler.hasMessages(EVENT_WIFI_RSSI_CHANGED)); - } - private void setWqmThreshold() { mThs1[0] = new Threshold( |