summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManish Dungriyal <mdungriyal@google.com>2023-01-27 12:19:13 +0000
committerManish Dungriyal <mdungriyal@google.com>2023-04-05 05:26:25 +0000
commitccc00093b1e6240d47902040328c04205ace9e2d (patch)
tree84db1837cb830058111fd526fe6334f233b437b1
parent27d15968aa33335e73cb95f59f62e2aa452d3381 (diff)
downloadTelephony-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)
-rw-r--r--services/QualifiedNetworksService/AndroidManifest.xml1
-rw-r--r--services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCallStatusTracker.java69
-rw-r--r--services/QualifiedNetworksService/src/com/android/telephony/qns/QnsComponents.java39
-rw-r--r--services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTimer.java415
-rw-r--r--services/QualifiedNetworksService/src/com/android/telephony/qns/QualifiedNetworksServiceImpl.java9
-rw-r--r--services/QualifiedNetworksService/src/com/android/telephony/qns/RestrictManager.java23
-rw-r--r--services/QualifiedNetworksService/src/com/android/telephony/qns/WifiBackhaulMonitor.java26
-rw-r--r--services/QualifiedNetworksService/src/com/android/telephony/qns/WifiQualityMonitor.java41
-rw-r--r--services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCallStatusTrackerTest.java29
-rw-r--r--services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsComponentsTest.java6
-rw-r--r--services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTest.java8
-rw-r--r--services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTimerTest.java309
-rw-r--r--services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QualifiedNetworksServiceImplTest.java1
-rw-r--r--services/QualifiedNetworksService/tests/src/com/android/telephony/qns/RestrictManagerTest.java26
-rw-r--r--services/QualifiedNetworksService/tests/src/com/android/telephony/qns/WifiBackhaulMonitorTest.java12
-rw-r--r--services/QualifiedNetworksService/tests/src/com/android/telephony/qns/WifiQualityMonitorTest.java27
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(