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

Commit 840d5655 authored by Jovana Knezevic's avatar Jovana Knezevic Committed by Android (Google) Code Review
Browse files

Merge "Adds automatic switching to Guest if user starts driving with the keyguard up."

parents bef47d02 edba98c1
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -209,6 +209,9 @@
    <!-- to read and change hvac values in a car -->
    <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />

    <!-- to be able to detect the driving state in a car-->
    <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" />

    <!-- Permission necessary to change car audio volume through CarAudioManager -->
    <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />

+5 −0
Original line number Diff line number Diff line
@@ -19,4 +19,9 @@
    <integer name="car_user_switcher_anim_cascade_delay_ms">27</integer>
    <!-- Full screen user switcher column number TODO: move to support library-->
    <integer name="user_fullscreen_switcher_num_col">3</integer>

    <!-- Number of milliseconds user can spend driving with the keyguard up. After that, we switch to Guest. -->
    <!-- If the number is negative, the feature is disabled.
         If it's zero, we switch to guest immediately as we start driving. -->
    <integer name="driving_on_keyguard_timeout_ms">30000</integer>
</resources>
+27 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.car;

import android.app.ActivityTaskManager;
import android.car.drivingstate.CarDrivingStateEvent;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.util.Log;
@@ -82,6 +83,8 @@ public class CarStatusBar extends StatusBar implements
    private DeviceProvisionedController mDeviceProvisionedController;
    private boolean mDeviceIsProvisioned = true;
    private HvacController mHvacController;
    private DrivingStateHelper mDrivingStateHelper;
    private SwitchToGuestTimer mSwitchToGuestTimer;

    @Override
    public void start() {
@@ -111,6 +114,12 @@ public class CarStatusBar extends StatusBar implements
                        }
                    });
        }

        // Register a listener for driving state changes.
        mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
        mDrivingStateHelper.connectToCarService();

        mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
    }

    /**
@@ -205,6 +214,7 @@ public class CarStatusBar extends StatusBar implements
        mCarBatteryController.stopListening();
        mConnectedDeviceSignalController.stopListening();
        mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackListener);
        mDrivingStateHelper.disconnectFromCarService();

        if (mNavigationBarWindow != null) {
            mWindowManager.removeViewImmediate(mNavigationBarWindow);
@@ -476,6 +486,20 @@ public class CarStatusBar extends StatusBar implements
        }
    }

    private void onDrivingStateChanged(CarDrivingStateEvent notUsed) {
        // Check if we need to start the timer every time driving state changes.
        startSwitchToGuestTimerIfDrivingOnKeyguard();
    }

    private void startSwitchToGuestTimerIfDrivingOnKeyguard() {
        if (mDrivingStateHelper.isCurrentlyDriving() && mState != StatusBarState.SHADE) {
            // We're driving while keyguard is up.
            mSwitchToGuestTimer.start();
        } else {
            mSwitchToGuestTimer.cancel();
        }
    }

    @Override
    protected void createUserSwitcher() {
        UserSwitcherController userSwitcherController =
@@ -491,6 +515,9 @@ public class CarStatusBar extends StatusBar implements
    @Override
    public void onStateChanged(int newState) {
        super.onStateChanged(newState);

        startSwitchToGuestTimerIfDrivingOnKeyguard();

        if (mFullscreenUserSwitcher == null) {
            return; // Not using the full screen user switcher.
        }
+127 −0
Original line number Diff line number Diff line
/*
 * 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.systemui.statusbar.car;

import android.car.Car;
import android.car.CarNotConnectedException;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarDrivingStateManager;
import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.NonNull;

/**
 * Helper class for connecting to the {@link CarDrivingStateManager} and listening for driving state
 * changes.
 */
public class DrivingStateHelper {
    public static final String TAG = "DrivingStateHelper";

    private final Context mContext;
    private CarDrivingStateManager mDrivingStateManager;
    private Car mCar;
    private CarDrivingStateEventListener mDrivingStateHandler;

    public DrivingStateHelper(Context context,
            @NonNull CarDrivingStateEventListener drivingStateHandler) {
        mContext = context;
        mDrivingStateHandler = drivingStateHandler;
    }

    /**
     * Queries {@link CarDrivingStateManager} for current driving state. Returns {@code true} if car
     * is idling or moving, {@code false} otherwise.
     */
    public boolean isCurrentlyDriving() {
        try {
            CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
            if (currentState != null) {
                return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
                        || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
            }
        } catch (CarNotConnectedException e) {
            Log.e(TAG, "Cannot determine current driving state. Car not connected", e);
        }

        return false; // Default to false.
    }

    /**
     * Establishes connection with the Car service.
     */
    public void connectToCarService() {
        mCar = Car.createCar(mContext, mCarConnectionListener);
        if (mCar != null) {
            mCar.connect();
        }
    }

    /**
     * Disconnects from Car service and cleans up listeners.
     */
    public void disconnectFromCarService() {
        if (mCar != null) {
            mCar.disconnect();
        }
    }

    private final ServiceConnection mCarConnectionListener =
            new ServiceConnection() {
                public void onServiceConnected(ComponentName name, IBinder service) {
                    logD("Car Service connected");
                    try {
                        mDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager(
                                Car.CAR_DRIVING_STATE_SERVICE);
                        if (mDrivingStateManager != null) {
                            mDrivingStateManager.registerListener(mDrivingStateHandler);
                            mDrivingStateHandler.onDrivingStateChanged(
                                    mDrivingStateManager.getCurrentCarDrivingState());
                        } else {
                            Log.e(TAG, "CarDrivingStateService service not available");
                        }
                    } catch (CarNotConnectedException e) {
                        Log.e(TAG, "Car not connected", e);
                    }
                }

                @Override
                public void onServiceDisconnected(ComponentName name) {
                    destroyDrivingStateManager();
                }
            };

    private void destroyDrivingStateManager() {
        try {
            if (mDrivingStateManager != null) {
                mDrivingStateManager.unregisterListener();
            }
        } catch (CarNotConnectedException e) {
            Log.e(TAG, "Error unregistering listeners", e);
        }
    }

    private void logD(String message) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, message);
        }
    }
}
 No newline at end of file
+116 −0
Original line number Diff line number Diff line
/*
 * 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.systemui.statusbar.car;

import android.car.userlib.CarUserManagerHelper;
import android.content.Context;
import android.os.CountDownTimer;
import android.util.Log;

import com.android.systemui.R;

import androidx.annotation.GuardedBy;

/**
 * Wrapper for a countdown timer that switches to Guest if the user has been driving with
 * the keyguard up for configurable number of seconds.
 */
public class SwitchToGuestTimer {
    private static final String TAG = "SwitchToGuestTimer";

    // After how many ms CountdownTimer.onTick gets triggered.
    private static final int COUNTDOWN_INTERVAL_MS = 1000;

    private final CarUserManagerHelper mCarUserManagerHelper;
    private final Object mTimerLock;
    private final String mGuestName;
    private final int mTimeoutMs;
    private final boolean mEnabled;

    @GuardedBy("mTimerLock")
    private CountDownTimer mSwitchToGuestTimer;

    public SwitchToGuestTimer(Context context) {
        mCarUserManagerHelper = new CarUserManagerHelper(context);
        mGuestName = context.getResources().getString(R.string.car_guest);
        mTimeoutMs = context.getResources().getInteger(R.integer.driving_on_keyguard_timeout_ms);

        // Lock prevents multiple timers being started.
        mTimerLock = new Object();

        // If milliseconds to switch is a negative number, the feature is disabled.
        mEnabled = mTimeoutMs >= 0;
    }

    /**
     * Starts the timer if it's not already running.
     */
    public void start() {
        if (!mEnabled) {
            logD("Switching to guest after driving on keyguard is disabled.");
            return;
        }

        synchronized (mTimerLock) {
            if (mSwitchToGuestTimer != null) {
                logD("Timer is already running.");
                return;
            }

            mSwitchToGuestTimer = new CountDownTimer(mTimeoutMs, COUNTDOWN_INTERVAL_MS) {
                @Override
                public void onTick(long msUntilFinished) {
                    logD("Ms until switching to guest: " + Long.toString(msUntilFinished));
                }

                @Override
                public void onFinish() {
                    mCarUserManagerHelper.startNewGuestSession(mGuestName);
                    cancel();
                }
            };

            logI("Starting timer");
            mSwitchToGuestTimer.start();
        }
    }

    /**
     * Cancels the running timer.
     */
    public void cancel() {
        synchronized (mTimerLock) {
            if (mSwitchToGuestTimer != null) {
                logI("Cancelling timer");
                mSwitchToGuestTimer.cancel();
                mSwitchToGuestTimer = null;
            }
        }
    }

    private void logD(String message) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, message);
        }
    }

    private void logI(String message) {
        if (Log.isLoggable(TAG, Log.INFO)) {
            Log.i(TAG, message);
        }
    }
}