Adds automatic switching to Guest if user starts driving with the keyguard

up.

driving_on_keyguard_timeout_ms controlls the number of milliseconds we wait,
before switching to Guest. If this number is negative, feature is disabled.

Change-Id: Ic1357362a97cb14a4f221d53e17a30cd3fefc5ea
Fixes: 110228676
Test: manual testing on mojave and emulator. Toggling driving state and keyguard, and observing the timer logs and switching.
This commit is contained in:
jovanak
2018-09-14 15:46:24 -07:00
parent 0d541559af
commit edba98c1c8
5 changed files with 278 additions and 0 deletions

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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.
}

View File

@@ -0,0 +1,127 @@
/*
* 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);
}
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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);
}
}
}