Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cfd25ee5 authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Exit doze during an emergency call." into udc-dev

parents c55dbe72 27ed45f3
Loading
Loading
Loading
Loading
+84 −6
Original line number Diff line number Diff line
@@ -79,6 +79,9 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -146,15 +149,17 @@ import java.util.stream.Collectors;
       label="deep";

       STATE_ACTIVE [
         label="STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon",
         label="STATE_ACTIVE\nScreen on OR charging OR alarm going off soon\n"
             + "OR active emergency call",
         color=black,shape=diamond
       ]
       STATE_INACTIVE [
         label="STATE_INACTIVE\nScreen off AND Not charging",color=black,shape=diamond
         label="STATE_INACTIVE\nScreen off AND not charging AND no active emergency call",
         color=black,shape=diamond
       ]
       STATE_QUICK_DOZE_DELAY [
         label="STATE_QUICK_DOZE_DELAY\n"
             + "Screen off AND Not charging\n"
             + "Screen off AND not charging AND no active emergency call\n"
             + "Location, motion detection, and significant motion monitoring turned off",
         color=black,shape=diamond
       ]
@@ -237,11 +242,12 @@ import java.util.stream.Collectors;
       label="light"

       LIGHT_STATE_ACTIVE [
         label="LIGHT_STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon",
         label="LIGHT_STATE_ACTIVE\n"
             + "Screen on OR charging OR alarm going off soon OR active emergency call",
         color=black,shape=diamond
       ]
       LIGHT_STATE_INACTIVE [
         label="LIGHT_STATE_INACTIVE\nScreen off AND Not charging",
         label="LIGHT_STATE_INACTIVE\nScreen off AND not charging AND no active emergency call",
         color=black,shape=diamond
       ]
       LIGHT_STATE_IDLE [label="LIGHT_STATE_IDLE\n",color=red,shape=box]
@@ -411,6 +417,7 @@ public class DeviceIdleController extends SystemService
    private static final int ACTIVE_REASON_FROM_BINDER_CALL = 5;
    private static final int ACTIVE_REASON_FORCED = 6;
    private static final int ACTIVE_REASON_ALARM = 7;
    private static final int ACTIVE_REASON_EMERGENCY_CALL = 8;
    @VisibleForTesting
    static final int SET_IDLE_FACTOR_RESULT_UNINIT = -1;
    @VisibleForTesting
@@ -765,6 +772,8 @@ public class DeviceIdleController extends SystemService
        }
    };

    private final EmergencyCallListener mEmergencyCallListener = new EmergencyCallListener();

    /** Post stationary status only to this listener. */
    private void postStationaryStatus(DeviceIdleInternal.StationaryListener listener) {
        mHandler.obtainMessage(MSG_REPORT_STATIONARY_STATUS, listener).sendToTarget();
@@ -2323,6 +2332,39 @@ public class DeviceIdleController extends SystemService
        }
    }

    private class EmergencyCallListener extends TelephonyCallback implements
            TelephonyCallback.OutgoingEmergencyCallListener,
            TelephonyCallback.CallStateListener {
        private volatile boolean mIsEmergencyCallActive;

        @Override
        public void onOutgoingEmergencyCall(EmergencyNumber placedEmergencyNumber,
                int subscriptionId) {
            mIsEmergencyCallActive = true;
            if (DEBUG) Slog.d(TAG, "onOutgoingEmergencyCall(): subId = " + subscriptionId);
            synchronized (DeviceIdleController.this) {
                mActiveReason = ACTIVE_REASON_EMERGENCY_CALL;
                becomeActiveLocked("emergency call", Process.myUid());
            }
        }

        @Override
        public void onCallStateChanged(int state) {
            if (DEBUG) Slog.d(TAG, "onCallStateChanged(): state is " + state);
            // An emergency call just finished
            if (state == TelephonyManager.CALL_STATE_IDLE && mIsEmergencyCallActive) {
                mIsEmergencyCallActive = false;
                synchronized (DeviceIdleController.this) {
                    becomeInactiveIfAppropriateLocked();
                }
            }
        }

        boolean isEmergencyCallActive() {
            return mIsEmergencyCallActive;
        }
    }

    static class Injector {
        private final Context mContext;
        private ConnectivityManager mConnectivityManager;
@@ -2406,6 +2448,10 @@ public class DeviceIdleController extends SystemService
            return mContext.getSystemService(SensorManager.class);
        }

        TelephonyManager getTelephonyManager() {
            return mContext.getSystemService(TelephonyManager.class);
        }

        ConstraintController getConstraintController(Handler handler,
                DeviceIdleInternal localService) {
            if (mContext.getPackageManager()
@@ -2634,6 +2680,9 @@ public class DeviceIdleController extends SystemService

                mLocalActivityTaskManager.registerScreenObserver(mScreenObserver);

                mInjector.getTelephonyManager().registerTelephonyCallback(
                        JobSchedulerBackgroundThread.getExecutor(), mEmergencyCallListener);

                passWhiteListsToForceAppStandbyTrackerLocked();
                updateInteractivityLocked();
            }
@@ -3435,6 +3484,7 @@ public class DeviceIdleController extends SystemService

        final boolean isScreenBlockingInactive =
                mScreenOn && (!mConstants.WAIT_FOR_UNLOCK || !mScreenLocked);
        final boolean isEmergencyCallActive = mEmergencyCallListener.isEmergencyCallActive();
        if (DEBUG) {
            Slog.d(TAG, "becomeInactiveIfAppropriateLocked():"
                    + " isScreenBlockingInactive=" + isScreenBlockingInactive
@@ -3442,10 +3492,11 @@ public class DeviceIdleController extends SystemService
                    + ", WAIT_FOR_UNLOCK=" + mConstants.WAIT_FOR_UNLOCK
                    + ", mScreenLocked=" + mScreenLocked + ")"
                    + " mCharging=" + mCharging
                    + " emergencyCall=" + isEmergencyCallActive
                    + " mForceIdle=" + mForceIdle
            );
        }
        if (!mForceIdle && (mCharging || isScreenBlockingInactive)) {
        if (!mForceIdle && (mCharging || isScreenBlockingInactive || isEmergencyCallActive)) {
            return;
        }
        // Become inactive and determine if we will ultimately go idle.
@@ -3568,6 +3619,17 @@ public class DeviceIdleController extends SystemService
        }
        EventLogTags.writeDeviceIdleLightStep();

        if (mEmergencyCallListener.isEmergencyCallActive()) {
            // The emergency call should have raised the state to ACTIVE and kept it there,
            // so this method shouldn't be called. Don't proceed further.
            Slog.wtf(TAG, "stepLightIdleStateLocked called when emergency call is active");
            if (mLightState != LIGHT_STATE_ACTIVE) {
                mActiveReason = ACTIVE_REASON_EMERGENCY_CALL;
                becomeActiveLocked("emergency", Process.myUid());
            }
            return;
        }

        switch (mLightState) {
            case LIGHT_STATE_INACTIVE:
                mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
@@ -3650,6 +3712,17 @@ public class DeviceIdleController extends SystemService
        if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
        EventLogTags.writeDeviceIdleStep();

        if (mEmergencyCallListener.isEmergencyCallActive()) {
            // The emergency call should have raised the state to ACTIVE and kept it there,
            // so this method shouldn't be called. Don't proceed further.
            Slog.wtf(TAG, "stepIdleStateLocked called when emergency call is active");
            if (mState != STATE_ACTIVE) {
                mActiveReason = ACTIVE_REASON_EMERGENCY_CALL;
                becomeActiveLocked("emergency", Process.myUid());
            }
            return;
        }

        if (isUpcomingAlarmClock()) {
            // Whoops, there is an upcoming alarm.  We don't actually want to go idle.
            if (mState != STATE_ACTIVE) {
@@ -3984,6 +4057,11 @@ public class DeviceIdleController extends SystemService
        }
    }

    @VisibleForTesting
    boolean isEmergencyCallActive() {
        return mEmergencyCallListener.isEmergencyCallActive();
    }

    @GuardedBy("this")
    boolean isOpsInactiveLocked() {
        return mActiveIdleOpCount <= 0 && !mJobsActive && !mAlarmsActive;
+148 −0
Original line number Diff line number Diff line
@@ -86,6 +86,9 @@ import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;

import androidx.test.runner.AndroidJUnit4;

@@ -119,6 +122,8 @@ public class DeviceIdleControllerTest {
    private AnyMotionDetectorForTest mAnyMotionDetector;
    private AppStateTrackerForTest mAppStateTracker;
    private DeviceIdleController.Constants mConstants;
    private TelephonyCallback.OutgoingEmergencyCallListener mEmergencyCallListener;
    private TelephonyCallback.CallStateListener mCallStateListener;
    private InjectorForTest mInjector;

    private MockitoSession mMockingSession;
@@ -140,6 +145,8 @@ public class DeviceIdleControllerTest {
    private Sensor mMotionSensor;
    @Mock
    private SensorManager mSensorManager;
    @Mock
    private TelephonyManager mTelephonyManager;

    class InjectorForTest extends DeviceIdleController.Injector {
        ConnectivityManager connectivityManager;
@@ -231,6 +238,11 @@ public class DeviceIdleControllerTest {
            return constraintController;
        }

        @Override
        TelephonyManager getTelephonyManager() {
            return mTelephonyManager;
        }

        @Override
        boolean useMotionSensor() {
            return true;
@@ -343,6 +355,15 @@ public class DeviceIdleControllerTest {

        // Get the same Constants object that mDeviceIdleController got.
        mConstants = mInjector.getConstants(mDeviceIdleController);

        final ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor =
                ArgumentCaptor.forClass(TelephonyCallback.class);
        verify(mTelephonyManager)
                .registerTelephonyCallback(any(), telephonyCallbackCaptor.capture());
        mEmergencyCallListener = (TelephonyCallback.OutgoingEmergencyCallListener)
                telephonyCallbackCaptor.getValue();
        mCallStateListener =
                (TelephonyCallback.CallStateListener) telephonyCallbackCaptor.getValue();
    }

    @After
@@ -531,6 +552,16 @@ public class DeviceIdleControllerTest {

        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
        verifyStateConditions(STATE_ACTIVE);

        // All other conditions allow for going INACTIVE...
        setAlarmSoon(false);
        setChargingOn(false);
        setScreenOn(false);
        // ...except the emergency call.
        setEmergencyCallActive(true);

        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
        verifyStateConditions(STATE_ACTIVE);
    }

    @Test
@@ -559,6 +590,15 @@ public class DeviceIdleControllerTest {

        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
        verifyLightStateConditions(LIGHT_STATE_ACTIVE);

        // All other conditions allow for going INACTIVE...
        setChargingOn(false);
        setScreenOn(false);
        // ...except the emergency call.
        setEmergencyCallActive(true);

        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
        verifyLightStateConditions(LIGHT_STATE_ACTIVE);
    }

    @Test
@@ -569,6 +609,7 @@ public class DeviceIdleControllerTest {
        setAlarmSoon(false);
        setChargingOn(false);
        setScreenOn(false);
        setEmergencyCallActive(false);

        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
        verifyStateConditions(STATE_INACTIVE);
@@ -613,6 +654,7 @@ public class DeviceIdleControllerTest {

        setChargingOn(false);
        setScreenOn(false);
        setEmergencyCallActive(false);

        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
        verifyLightStateConditions(LIGHT_STATE_INACTIVE);
@@ -1147,6 +1189,22 @@ public class DeviceIdleControllerTest {
                eq(true));
    }

    @Test
    public void testEmergencyCallEndTriggersInactive() {
        setAlarmSoon(false);
        setChargingOn(false);
        setScreenOn(false);
        setEmergencyCallActive(true);

        verifyStateConditions(STATE_ACTIVE);
        verifyLightStateConditions(LIGHT_STATE_ACTIVE);

        setEmergencyCallActive(false);

        verifyStateConditions(STATE_INACTIVE);
        verifyLightStateConditions(LIGHT_STATE_INACTIVE);
    }

    ///////////////// EXIT conditions ///////////////////

    @Test
@@ -2096,6 +2154,75 @@ public class DeviceIdleControllerTest {
                .onDeviceStationaryChanged(eq(true));
    }

    @Test
    public void testEmergencyEndsIdle() {
        enterDeepState(STATE_ACTIVE);
        setEmergencyCallActive(true);
        verifyStateConditions(STATE_ACTIVE);

        enterDeepState(STATE_INACTIVE);
        setEmergencyCallActive(true);
        verifyStateConditions(STATE_ACTIVE);

        enterDeepState(STATE_IDLE_PENDING);
        setEmergencyCallActive(true);
        verifyStateConditions(STATE_ACTIVE);

        enterDeepState(STATE_SENSING);
        setEmergencyCallActive(true);
        verifyStateConditions(STATE_ACTIVE);

        enterDeepState(STATE_LOCATING);
        setEmergencyCallActive(true);
        verifyStateConditions(STATE_ACTIVE);

        // Quick doze enabled or not shouldn't affect the end state.
        enterDeepState(STATE_QUICK_DOZE_DELAY);
        setQuickDozeEnabled(true);
        setEmergencyCallActive(true);
        verifyStateConditions(STATE_ACTIVE);

        enterDeepState(STATE_QUICK_DOZE_DELAY);
        setQuickDozeEnabled(false);
        setEmergencyCallActive(true);
        verifyStateConditions(STATE_ACTIVE);

        enterDeepState(STATE_IDLE);
        setEmergencyCallActive(true);
        verifyStateConditions(STATE_ACTIVE);

        enterDeepState(STATE_IDLE_MAINTENANCE);
        setEmergencyCallActive(true);
        verifyStateConditions(STATE_ACTIVE);
    }

    @Test
    public void testEmergencyEndsLightIdle() {
        enterLightState(LIGHT_STATE_ACTIVE);
        setEmergencyCallActive(true);
        verifyLightStateConditions(LIGHT_STATE_ACTIVE);

        enterLightState(LIGHT_STATE_INACTIVE);
        setEmergencyCallActive(true);
        verifyLightStateConditions(LIGHT_STATE_ACTIVE);

        enterLightState(LIGHT_STATE_WAITING_FOR_NETWORK);
        setEmergencyCallActive(true);
        verifyLightStateConditions(LIGHT_STATE_ACTIVE);

        enterLightState(LIGHT_STATE_IDLE);
        setEmergencyCallActive(true);
        verifyLightStateConditions(LIGHT_STATE_ACTIVE);

        enterLightState(LIGHT_STATE_IDLE_MAINTENANCE);
        setEmergencyCallActive(true);
        verifyLightStateConditions(LIGHT_STATE_ACTIVE);

        enterLightState(LIGHT_STATE_OVERRIDE);
        setEmergencyCallActive(true);
        verifyLightStateConditions(LIGHT_STATE_ACTIVE);
    }

    private void enterDeepState(int state) {
        switch (state) {
            case STATE_ACTIVE:
@@ -2108,6 +2235,7 @@ public class DeviceIdleControllerTest {
                setQuickDozeEnabled(true);
                setScreenOn(false);
                setChargingOn(false);
                setEmergencyCallActive(false);
                mDeviceIdleController.becomeInactiveIfAppropriateLocked();
                break;
            case STATE_LOCATING:
@@ -2128,6 +2256,7 @@ public class DeviceIdleControllerTest {
                setQuickDozeEnabled(false);
                setScreenOn(false);
                setChargingOn(false);
                setEmergencyCallActive(false);
                mDeviceIdleController.becomeInactiveIfAppropriateLocked();
                int count = 0;
                while (mDeviceIdleController.getState() != state) {
@@ -2159,6 +2288,7 @@ public class DeviceIdleControllerTest {
                enterLightState(LIGHT_STATE_ACTIVE);
                setScreenOn(false);
                setChargingOn(false);
                setEmergencyCallActive(false);
                int count = 0;
                mDeviceIdleController.becomeInactiveIfAppropriateLocked();
                while (mDeviceIdleController.getLightState() != lightState) {
@@ -2177,6 +2307,7 @@ public class DeviceIdleControllerTest {
            case LIGHT_STATE_OVERRIDE:
                setScreenOn(false);
                setChargingOn(false);
                setEmergencyCallActive(false);
                mDeviceIdleController.setLightStateForTest(lightState);
                break;
            default:
@@ -2188,6 +2319,14 @@ public class DeviceIdleControllerTest {
        mDeviceIdleController.updateChargingLocked(on);
    }

    private void setEmergencyCallActive(boolean active) {
        if (active) {
            mEmergencyCallListener.onOutgoingEmergencyCall(mock(EmergencyNumber.class), 0);
        } else {
            mCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE);
        }
    }

    private void setScreenLocked(boolean locked) {
        mDeviceIdleController.keyguardShowingLocked(locked);
    }
@@ -2235,6 +2374,7 @@ public class DeviceIdleControllerTest {
                assertFalse(mDeviceIdleController.isCharging());
                assertFalse(mDeviceIdleController.isScreenOn()
                        && !mDeviceIdleController.isKeyguardShowing());
                assertFalse(mDeviceIdleController.isEmergencyCallActive());
                break;
            case STATE_IDLE_PENDING:
                assertEquals(
@@ -2244,6 +2384,7 @@ public class DeviceIdleControllerTest {
                assertFalse(mDeviceIdleController.isCharging());
                assertFalse(mDeviceIdleController.isScreenOn()
                        && !mDeviceIdleController.isKeyguardShowing());
                assertFalse(mDeviceIdleController.isEmergencyCallActive());
                break;
            case STATE_SENSING:
                assertEquals(
@@ -2255,6 +2396,7 @@ public class DeviceIdleControllerTest {
                assertFalse(mDeviceIdleController.isCharging());
                assertFalse(mDeviceIdleController.isScreenOn()
                        && !mDeviceIdleController.isKeyguardShowing());
                assertFalse(mDeviceIdleController.isEmergencyCallActive());
                break;
            case STATE_LOCATING:
                assertEquals(
@@ -2263,6 +2405,7 @@ public class DeviceIdleControllerTest {
                assertFalse(mDeviceIdleController.isCharging());
                assertFalse(mDeviceIdleController.isScreenOn()
                        && !mDeviceIdleController.isKeyguardShowing());
                assertFalse(mDeviceIdleController.isEmergencyCallActive());
                break;
            case STATE_IDLE:
                if (mDeviceIdleController.hasMotionSensor()) {
@@ -2276,6 +2419,7 @@ public class DeviceIdleControllerTest {
                        && !mDeviceIdleController.isKeyguardShowing());
                // Light state should be OVERRIDE at this point.
                verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
                assertFalse(mDeviceIdleController.isEmergencyCallActive());
                break;
            case STATE_IDLE_MAINTENANCE:
                if (mDeviceIdleController.hasMotionSensor()) {
@@ -2287,6 +2431,7 @@ public class DeviceIdleControllerTest {
                assertFalse(mDeviceIdleController.isCharging());
                assertFalse(mDeviceIdleController.isScreenOn()
                        && !mDeviceIdleController.isKeyguardShowing());
                assertFalse(mDeviceIdleController.isEmergencyCallActive());
                break;
            case STATE_QUICK_DOZE_DELAY:
                // If quick doze is enabled, the motion listener should NOT be active.
@@ -2295,6 +2440,7 @@ public class DeviceIdleControllerTest {
                assertFalse(mDeviceIdleController.isCharging());
                assertFalse(mDeviceIdleController.isScreenOn()
                        && !mDeviceIdleController.isKeyguardShowing());
                assertFalse(mDeviceIdleController.isEmergencyCallActive());
                break;
            default:
                fail("Conditions for " + stateToString(expectedState) + " unknown.");
@@ -2312,6 +2458,7 @@ public class DeviceIdleControllerTest {
            case LIGHT_STATE_ACTIVE:
                assertTrue(
                        mDeviceIdleController.isCharging() || mDeviceIdleController.isScreenOn()
                                || mDeviceIdleController.isEmergencyCallActive()
                                // Or there's an alarm coming up soon.
                                || SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM
                                > mAlarmManager.getNextWakeFromIdleTime());
@@ -2324,6 +2471,7 @@ public class DeviceIdleControllerTest {
                assertFalse(mDeviceIdleController.isCharging());
                assertFalse(mDeviceIdleController.isScreenOn()
                        && !mDeviceIdleController.isKeyguardShowing());
                assertFalse(mDeviceIdleController.isEmergencyCallActive());
                break;
            default:
                fail("Conditions for " + lightStateToString(expectedLightState) + " unknown.");