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:
@@ -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" />
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user