blob: e5e147969efa91b5114a13c3c237422741415bfd [file] [log] [blame]
/*
* Copyright (C) 2018 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.ans;
import android.app.AlarmManager;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.telephony.Rlog;
import com.android.internal.annotations.VisibleForTesting;
import java.util.concurrent.TimeUnit;
/**
* ANSServiceStateEvaluator class which will evaluate the data service state of a subId
* compared to another.
*/
public class ANSServiceStateEvaluator {
private Context mContext;
private final Object mLock = new Object();
private int mOppDataSubId;
private int mPrimarySubId;
/* new opportunistic data service state */
private int mOppDataNewState = ANSServiceStateMonitor.SERVICE_STATE_UNKNOWN;
private int mPrimaryNewState = ANSServiceStateMonitor.SERVICE_STATE_UNKNOWN;
private boolean mIsWaitingForTimeout = false;
@VisibleForTesting
protected ANSServiceEvaluatorCallback mServiceEvaluatorCallback;
@VisibleForTesting
protected ANSServiceStateMonitor mOppDataSubMonitor;
@VisibleForTesting
protected ANSServiceStateMonitor mPrimarySubMonitor;
@VisibleForTesting
protected AlarmManager mAlarmManager;
private static final int WAIT_FOR_DATA_SERVICE_PERIOD = (int) TimeUnit.SECONDS.toMillis(10);
private static final String LOG_TAG = "ANSServiceStateEvaluator";
private static final boolean DBG = true;
/* message to indicate no data for WAIT_FOR_DATA_SERVICE_PERIOD */
private static final int MSG_WAIT_FOR_DATA_SERVICE_TIMOUT = 1;
/**
* call back to confirm service state evaluation
*/
public interface ANSServiceEvaluatorCallback {
/**
* call back to confirm bad service
*/
void onBadDataService();
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_WAIT_FOR_DATA_SERVICE_TIMOUT:
mIsWaitingForTimeout = false;
logDebug("Msg received to get data");
evaluateUpdatedState();
break;
default:
log("invalid message");
break;
}
}
};
public final AlarmManager.OnAlarmListener mDataServiceWaitTimer =
(AlarmManager.OnAlarmListener) () -> {
logDebug("Alarm fired");
mHandler.sendEmptyMessage(MSG_WAIT_FOR_DATA_SERVICE_TIMOUT);
};
/**
* set alarm to wait for data service
*/
private void setDataServiceWaitAlarm() {
mAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis()
+ WAIT_FOR_DATA_SERVICE_PERIOD, LOG_TAG, mDataServiceWaitTimer, null);
}
/**
* stop the alarm
*/
private void stopDataServiceWaitAlarm() {
mAlarmManager.cancel(mDataServiceWaitTimer);
}
private boolean evaluateIfBadServiceState() {
/* if we have not received update on both subId, we can not take decision, yes */
log("evaluateIfBadServiceState: mPrimaryNewState: "
+ ANSServiceStateMonitor.getStateString(mPrimaryNewState) + " mOppDataNewState: "
+ ANSServiceStateMonitor.getStateString(mOppDataNewState));
if ((mPrimaryNewState == ANSServiceStateMonitor.SERVICE_STATE_UNKNOWN)
|| (mOppDataNewState == ANSServiceStateMonitor.SERVICE_STATE_UNKNOWN)) {
return false;
}
/* Evaluate if primary subscription has good service and if
opportunistic data subscription is not, if yes return true.
*/
switch (mPrimaryNewState) {
case ANSServiceStateMonitor.SERVICE_STATE_NO_SERVICE:
/* no need to make any change */
return false;
case ANSServiceStateMonitor.SERVICE_STATE_BAD:
if (mOppDataNewState != ANSServiceStateMonitor.SERVICE_STATE_NO_SERVICE) {
return false;
}
break;
case ANSServiceStateMonitor.SERVICE_STATE_GOOD:
if (mOppDataNewState == ANSServiceStateMonitor.SERVICE_STATE_GOOD) {
return false;
}
break;
default:
log("invalid state");
break;
}
return true;
}
private void evaluateUpdatedState() {
logDebug("evaluateUpdatedState " + mIsWaitingForTimeout);
if (!mIsWaitingForTimeout && evaluateIfBadServiceState()) {
mServiceEvaluatorCallback.onBadDataService();
}
}
/* service monitor callback will get called for service state change on a particular subId. */
@VisibleForTesting
protected ANSServiceStateMonitor.ANSServiceMonitorCallback mServiceMonitorCallback =
new ANSServiceStateMonitor.ANSServiceMonitorCallback() {
@Override
public void onServiceMonitorUpdate(int subId, int state) {
logDebug("onServiceMonitorUpdate subId: " + subId + " state: "
+ ANSServiceStateMonitor.getStateString(state));
synchronized (mLock) {
if (mServiceEvaluatorCallback == null) {
return;
}
if (subId == mPrimarySubId) {
mPrimaryNewState = state;
} else if (subId == mOppDataSubId) {
mOppDataNewState = state;
} else {
logDebug("invalid sub id");
}
evaluateUpdatedState();
}
}
};
public ANSServiceStateEvaluator(Context c,
ANSServiceEvaluatorCallback serviceEvaluatorCallback) {
init(c, serviceEvaluatorCallback);
}
@VisibleForTesting
protected void init(Context c, ANSServiceEvaluatorCallback serviceEvaluatorCallback) {
mContext = c;
mServiceEvaluatorCallback = serviceEvaluatorCallback;
mOppDataSubMonitor = new ANSServiceStateMonitor(mContext, mServiceMonitorCallback);
mPrimarySubMonitor = new ANSServiceStateMonitor(mContext, mServiceMonitorCallback);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
}
/**
* start service state evaluation for dataSubId compared to voiceSubId.
* This API evaluates the service state conditions of dataSubId and decides whether
* data service is bad compared to voiceSubId
* @param dataSubId current data subscription id
* @param voiceSubId voice subscription id
*/
public void startEvaluation(int dataSubId, int voiceSubId) {
logDebug("Start evaluation");
/* make sure to clean up if there is any evaluation going on. */
stopEvaluation();
setDataServiceWaitAlarm();
synchronized (mLock) {
mIsWaitingForTimeout = true;
mOppDataSubId = dataSubId;
mPrimarySubId = voiceSubId;
mOppDataSubMonitor.startListeningForNetworkConditionChange(dataSubId);
mPrimarySubMonitor.startListeningForNetworkConditionChange(voiceSubId);
}
}
/**
* stop service state evaluation
*/
public void stopEvaluation() {
logDebug("Stop evaluation");
synchronized (mLock) {
mOppDataSubId = -1;
mPrimarySubId = -1;
if (mIsWaitingForTimeout) {
stopDataServiceWaitAlarm();
}
mIsWaitingForTimeout = false;
mOppDataSubMonitor.stopListeningForNetworkConditionChange();
mPrimarySubMonitor.stopListeningForNetworkConditionChange();
mOppDataNewState = ANSServiceStateMonitor.SERVICE_STATE_UNKNOWN;
mPrimaryNewState = ANSServiceStateMonitor.SERVICE_STATE_UNKNOWN;
}
}
private void log(String msg) {
Rlog.d(LOG_TAG, msg);
}
private void logDebug(String msg) {
if (DBG) {
Rlog.d(LOG_TAG, msg);
}
}
}