Skip to content
Snippets Groups Projects
Verified Commit 04ffff09 authored by Torsten Grote's avatar Torsten Grote
Browse files

Screen Lock: Lock after customizable inactivity timeout

parent e5112ae9
No related branches found
No related tags found
No related merge requests found
...@@ -26,6 +26,7 @@ import org.briarproject.briar.R; ...@@ -26,6 +26,7 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.logout.HideUiActivity; import org.briarproject.briar.android.logout.HideUiActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.LockManager;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
...@@ -54,6 +55,7 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.FAIL ...@@ -54,6 +55,7 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.FAIL
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_ID;
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID;
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_NOTIFICATION_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_NOTIFICATION_ID;
import static org.briarproject.briar.api.android.LockManager.ACTION_LOCK;
public class BriarService extends Service { public class BriarService extends Service {
...@@ -77,6 +79,8 @@ public class BriarService extends Service { ...@@ -77,6 +79,8 @@ public class BriarService extends Service {
AndroidNotificationManager notificationManager; AndroidNotificationManager notificationManager;
@Inject @Inject
AccountManager accountManager; AccountManager accountManager;
@Inject
LockManager lockManager;
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@Inject @Inject
...@@ -189,6 +193,9 @@ public class BriarService extends Service { ...@@ -189,6 +193,9 @@ public class BriarService extends Service {
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_LOCK.equals(intent.getAction())) {
lockManager.setLocked(true);
}
return START_NOT_STICKY; // Don't restart automatically if killed return START_NOT_STICKY; // Don't restart automatically if killed
} }
......
package org.briarproject.briar.android.account; package org.briarproject.briar.android.account;
import android.app.AlarmManager;
import android.app.Application; import android.app.Application;
import android.app.PendingIntent;
import android.arch.lifecycle.LiveData; import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData; import android.arch.lifecycle.MutableLiveData;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
...@@ -16,6 +19,8 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; ...@@ -16,6 +19,8 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.briar.R;
import org.briarproject.briar.android.BriarService;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.LockManager; import org.briarproject.briar.api.android.LockManager;
...@@ -25,9 +30,15 @@ import java.util.logging.Logger; ...@@ -25,9 +30,15 @@ import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.PendingIntent.getService;
import static android.content.Context.ALARM_SERVICE;
import static android.os.SystemClock.elapsedRealtime;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.settings.SettingsFragment.PREF_SCREEN_LOCK; import static org.briarproject.briar.android.settings.SettingsFragment.PREF_SCREEN_LOCK;
import static org.briarproject.briar.android.settings.SettingsFragment.PREF_SCREEN_LOCK_TIMEOUT;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
import static org.briarproject.briar.android.util.UiUtils.hasScreenLock; import static org.briarproject.briar.android.util.UiUtils.hasScreenLock;
...@@ -44,9 +55,14 @@ public class LockManagerImpl implements LockManager, Service, EventListener { ...@@ -44,9 +55,14 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
private final AndroidNotificationManager notificationManager; private final AndroidNotificationManager notificationManager;
@DatabaseExecutor @DatabaseExecutor
private final Executor dbExecutor; private final Executor dbExecutor;
private final AlarmManager alarmManager;
private final PendingIntent lockIntent;
private final int timeoutNever, timeoutDefault;
private volatile boolean locked = false; private volatile boolean locked = false;
private volatile boolean lockableSetting = false; private volatile boolean lockableSetting = false;
private volatile int timeoutMinutes;
private int activitiesRunning = 0;
private final MutableLiveData<Boolean> lockable = new MutableLiveData<>(); private final MutableLiveData<Boolean> lockable = new MutableLiveData<>();
@Inject @Inject
...@@ -57,6 +73,16 @@ public class LockManagerImpl implements LockManager, Service, EventListener { ...@@ -57,6 +73,16 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.notificationManager = notificationManager; this.notificationManager = notificationManager;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.alarmManager =
(AlarmManager) appContext.getSystemService(ALARM_SERVICE);
Intent i =
new Intent(ACTION_LOCK, null, appContext, BriarService.class);
this.lockIntent = getService(appContext, 0, i, 0);
this.timeoutNever = Integer.valueOf(
appContext.getString(R.string.pref_lock_timeout_value_never));
this.timeoutDefault = Integer.valueOf(
appContext.getString(R.string.pref_lock_timeout_value_default));
this.timeoutMinutes = timeoutNever;
// setting this in the constructor makes #getValue() @NonNull // setting this in the constructor makes #getValue() @NonNull
this.lockable.setValue(false); this.lockable.setValue(false);
...@@ -72,6 +98,26 @@ public class LockManagerImpl implements LockManager, Service, EventListener { ...@@ -72,6 +98,26 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
public void stopService() { public void stopService() {
} }
@UiThread
@Override
public void onActivityStart() {
activitiesRunning++;
alarmManager.cancel(lockIntent);
}
@UiThread
@Override
public void onActivityStop() {
activitiesRunning--;
if (activitiesRunning == 0 && !locked &&
timeoutMinutes != timeoutNever && lockable.getValue()) {
alarmManager.cancel(lockIntent);
long triggerAt =
elapsedRealtime() + MINUTES.toMillis(timeoutMinutes);
alarmManager.set(ELAPSED_REALTIME, triggerAt, lockIntent);
}
}
@Override @Override
public LiveData<Boolean> isLockable() { public LiveData<Boolean> isLockable() {
return lockable; return lockable;
...@@ -118,9 +164,13 @@ public class LockManagerImpl implements LockManager, Service, EventListener { ...@@ -118,9 +164,13 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
try { try {
Settings settings = Settings settings =
settingsManager.getSettings(SETTINGS_NAMESPACE); settingsManager.getSettings(SETTINGS_NAMESPACE);
// is the app lockable?
lockableSetting = settings.getBoolean(PREF_SCREEN_LOCK, false); lockableSetting = settings.getBoolean(PREF_SCREEN_LOCK, false);
boolean newValue = hasScreenLock(appContext) && lockableSetting; boolean newValue = hasScreenLock(appContext) && lockableSetting;
lockable.postValue(newValue); lockable.postValue(newValue);
// what is the timeout in minutes?
timeoutMinutes = settings.getInt(PREF_SCREEN_LOCK_TIMEOUT,
timeoutDefault);
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
lockableSetting = false; lockableSetting = false;
......
...@@ -53,6 +53,12 @@ public abstract class BriarActivity extends BaseActivity { ...@@ -53,6 +53,12 @@ public abstract class BriarActivity extends BaseActivity {
@Inject @Inject
protected LockManager lockManager; protected LockManager lockManager;
@Override
public void onStart() {
super.onStart();
lockManager.onActivityStart();
}
@Override @Override
protected void onActivityResult(int request, int result, Intent data) { protected void onActivityResult(int request, int result, Intent data) {
super.onActivityResult(request, result, data); super.onActivityResult(request, result, data);
...@@ -97,6 +103,12 @@ public abstract class BriarActivity extends BaseActivity { ...@@ -97,6 +103,12 @@ public abstract class BriarActivity extends BaseActivity {
} }
} }
@Override
protected void onStop() {
super.onStop();
lockManager.onActivityStop();
}
public void setSceneTransitionAnimation() { public void setSceneTransitionAnimation() {
if (SDK_INT < 21) return; if (SDK_INT < 21) return;
// workaround for #1007 // workaround for #1007
......
...@@ -15,6 +15,7 @@ import android.support.v4.content.ContextCompat; ...@@ -15,6 +15,7 @@ import android.support.v4.content.ContextCompat;
import android.support.v4.text.TextUtilsCompat; import android.support.v4.text.TextUtilsCompat;
import android.support.v7.preference.ListPreference; import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import android.support.v7.preference.PreferenceFragmentCompat; import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceGroup;
import android.view.LayoutInflater; import android.view.LayoutInflater;
...@@ -105,13 +106,15 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF ...@@ -105,13 +106,15 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class SettingsFragment extends PreferenceFragmentCompat public class SettingsFragment extends PreferenceFragmentCompat
implements EventListener, Preference.OnPreferenceChangeListener { implements EventListener, OnPreferenceChangeListener {
public static final String SETTINGS_NAMESPACE = "android-ui"; public static final String SETTINGS_NAMESPACE = "android-ui";
public static final String BT_NAMESPACE = BluetoothConstants.ID.getString(); public static final String BT_NAMESPACE = BluetoothConstants.ID.getString();
public static final String TOR_NAMESPACE = TorConstants.ID.getString(); public static final String TOR_NAMESPACE = TorConstants.ID.getString();
public static final String LANGUAGE = "pref_key_language"; public static final String LANGUAGE = "pref_key_language";
public static final String PREF_SCREEN_LOCK = "pref_key_lock"; public static final String PREF_SCREEN_LOCK = "pref_key_lock";
public static final String PREF_SCREEN_LOCK_TIMEOUT =
"pref_key_lock_timeout";
public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in"; public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in";
public static final String TOR_LOCATION = "pref_key_tor_location"; public static final String TOR_LOCATION = "pref_key_tor_location";
...@@ -124,6 +127,7 @@ public class SettingsFragment extends PreferenceFragmentCompat ...@@ -124,6 +127,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
private ListPreference torNetwork; private ListPreference torNetwork;
private SwitchPreference torBlocked; private SwitchPreference torBlocked;
private SwitchPreference screenLock; private SwitchPreference screenLock;
private ListPreference screenLockTimeout;
private SwitchPreference notifyPrivateMessages; private SwitchPreference notifyPrivateMessages;
private SwitchPreference notifyGroupMessages; private SwitchPreference notifyGroupMessages;
private SwitchPreference notifyForumPosts; private SwitchPreference notifyForumPosts;
...@@ -166,6 +170,8 @@ public class SettingsFragment extends PreferenceFragmentCompat ...@@ -166,6 +170,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
SwitchPreference notifySignIn = SwitchPreference notifySignIn =
(SwitchPreference) findPreference(NOTIFY_SIGN_IN); (SwitchPreference) findPreference(NOTIFY_SIGN_IN);
screenLock = (SwitchPreference) findPreference(PREF_SCREEN_LOCK); screenLock = (SwitchPreference) findPreference(PREF_SCREEN_LOCK);
screenLockTimeout =
(ListPreference) findPreference(PREF_SCREEN_LOCK_TIMEOUT);
notifyPrivateMessages = (SwitchPreference) findPreference( notifyPrivateMessages = (SwitchPreference) findPreference(
"pref_key_notify_private_messages"); "pref_key_notify_private_messages");
notifyGroupMessages = (SwitchPreference) findPreference( notifyGroupMessages = (SwitchPreference) findPreference(
...@@ -203,6 +209,7 @@ public class SettingsFragment extends PreferenceFragmentCompat ...@@ -203,6 +209,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
torNetwork.setOnPreferenceChangeListener(this); torNetwork.setOnPreferenceChangeListener(this);
torBlocked.setOnPreferenceChangeListener(this); torBlocked.setOnPreferenceChangeListener(this);
screenLock.setOnPreferenceChangeListener(this); screenLock.setOnPreferenceChangeListener(this);
screenLockTimeout.setOnPreferenceChangeListener(this);
if (SDK_INT >= 21) { if (SDK_INT >= 21) {
notifyLockscreen.setVisible(true); notifyLockscreen.setVisible(true);
notifyLockscreen.setOnPreferenceChangeListener(this); notifyLockscreen.setOnPreferenceChangeListener(this);
...@@ -224,6 +231,7 @@ public class SettingsFragment extends PreferenceFragmentCompat ...@@ -224,6 +231,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
theme.setVisible(FEATURE_FLAG_DARK_THEME); theme.setVisible(FEATURE_FLAG_DARK_THEME);
notifySignIn.setVisible(FEATURE_FLAG_SIGN_IN_REMINDER); notifySignIn.setVisible(FEATURE_FLAG_SIGN_IN_REMINDER);
screenLock.setVisible(FEATURE_FLAG_PIN_LOCK); screenLock.setVisible(FEATURE_FLAG_PIN_LOCK);
screenLockTimeout.setVisible(FEATURE_FLAG_PIN_LOCK);
findPreference("pref_key_explode").setVisible(false); findPreference("pref_key_explode").setVisible(false);
findPreference("pref_key_test_data").setVisible(false); findPreference("pref_key_test_data").setVisible(false);
...@@ -419,6 +427,7 @@ public class SettingsFragment extends PreferenceFragmentCompat ...@@ -419,6 +427,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
// - pref_key_notify_sign_in // - pref_key_notify_sign_in
// preferences partly needed here, because they have their own logic // preferences partly needed here, because they have their own logic
// - pref_key_lock (screenLock -> displayScreenLockSetting()) // - pref_key_lock (screenLock -> displayScreenLockSetting())
// - pref_key_lock_timeout (screenLockTimeout)
enableBluetooth.setEnabled(enabled); enableBluetooth.setEnabled(enabled);
torNetwork.setEnabled(enabled); torNetwork.setEnabled(enabled);
torBlocked.setEnabled(enabled); torBlocked.setEnabled(enabled);
...@@ -435,6 +444,7 @@ public class SettingsFragment extends PreferenceFragmentCompat ...@@ -435,6 +444,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
private void displayScreenLockSetting() { private void displayScreenLockSetting() {
if (SDK_INT < 21) { if (SDK_INT < 21) {
screenLock.setVisible(false); screenLock.setVisible(false);
screenLockTimeout.setVisible(false);
} else { } else {
if (getActivity() != null && hasScreenLock(getActivity())) { if (getActivity() != null && hasScreenLock(getActivity())) {
screenLock.setEnabled(true); screenLock.setEnabled(true);
...@@ -446,6 +456,11 @@ public class SettingsFragment extends PreferenceFragmentCompat ...@@ -446,6 +456,11 @@ public class SettingsFragment extends PreferenceFragmentCompat
screenLock.setChecked(false); screenLock.setChecked(false);
screenLock.setSummary(R.string.pref_lock_disabled_summary); screenLock.setSummary(R.string.pref_lock_disabled_summary);
} }
// timeout depends on screenLock and gets disabled automatically
int timeout = settings.getInt(PREF_SCREEN_LOCK_TIMEOUT,
Integer.valueOf(getString(
R.string.pref_lock_timeout_value_default)));
screenLockTimeout.setValue(String.valueOf(timeout));
} }
} }
...@@ -508,6 +523,11 @@ public class SettingsFragment extends PreferenceFragmentCompat ...@@ -508,6 +523,11 @@ public class SettingsFragment extends PreferenceFragmentCompat
Settings s = new Settings(); Settings s = new Settings();
s.putBoolean(PREF_SCREEN_LOCK, (Boolean) newValue); s.putBoolean(PREF_SCREEN_LOCK, (Boolean) newValue);
storeSettings(s); storeSettings(s);
} else if (preference == screenLockTimeout) {
Settings s = new Settings();
s.putInt(PREF_SCREEN_LOCK_TIMEOUT,
Integer.valueOf((String) newValue));
storeSettings(s);
} else if (preference == notifyPrivateMessages) { } else if (preference == notifyPrivateMessages) {
Settings s = new Settings(); Settings s = new Settings();
s.putBoolean(PREF_NOTIFY_PRIVATE, (Boolean) newValue); s.putBoolean(PREF_NOTIFY_PRIVATE, (Boolean) newValue);
......
package org.briarproject.briar.api.android; package org.briarproject.briar.api.android;
import android.app.Activity;
import android.arch.lifecycle.LiveData; import android.arch.lifecycle.LiveData;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
public interface LockManager { public interface LockManager {
String ACTION_LOCK = "lock";
/**
* Stops the inactivity timer when the user interacts with the app.
* Should typically be called by {@link Activity#onStart()}
*/
@UiThread
void onActivityStart();
/**
* Starts the inactivity timer which will lock the app.
* Should typically be called by {@link Activity#onStop()}
*/
@UiThread
void onActivityStop();
/** /**
* Returns an observable LiveData to indicate whether the app can be locked. * Returns an observable LiveData to indicate whether the app can be locked.
*/ */
......
...@@ -67,4 +67,23 @@ ...@@ -67,4 +67,23 @@
<item>@string/pref_theme_auto_value</item> <item>@string/pref_theme_auto_value</item>
<item>@string/pref_theme_system_value</item> <item>@string/pref_theme_system_value</item>
</string-array> </string-array>
<string-array name="pref_key_lock_timeout_entries">
<item>@string/pref_lock_timeout_never</item>
<item>@string/pref_lock_timeout_1</item>
<item>@string/pref_lock_timeout_5</item>
<item>@string/pref_lock_timeout_15</item>
<item>@string/pref_lock_timeout_30</item>
<item>@string/pref_lock_timeout_60</item>
</string-array>
<string name="pref_lock_timeout_value_default">5</string>
<string name="pref_lock_timeout_value_never">-1</string>
<string-array name="pref_key_lock_timeout_values">
<item>@string/pref_lock_timeout_value_never</item>
<item>1</item>
<item>5</item>
<item>15</item>
<item>30</item>
<item>60</item>
</string-array>
</resources> </resources>
...@@ -360,6 +360,15 @@ ...@@ -360,6 +360,15 @@
<string name="pref_lock_title">Screen Lock</string> <string name="pref_lock_title">Screen Lock</string>
<string name="pref_lock_summary">Use the device\'s screen lock to protect Briar while signed in</string> <string name="pref_lock_summary">Use the device\'s screen lock to protect Briar while signed in</string>
<string name="pref_lock_disabled_summary">Set up a screen lock for your device to protect Briar while signed in</string> <string name="pref_lock_disabled_summary">Set up a screen lock for your device to protect Briar while signed in</string>
<string name="pref_lock_timeout_title">Screen Lock Inactivity Timeout</string>
<string name="pref_lock_timeout_summary">When not using Briar, automatically lock it %s</string>
<string name="pref_lock_timeout_never">never</string>
<string name="pref_lock_timeout_1">after 1 Minute</string>
<string name="pref_lock_timeout_5">after 5 Minutes</string>
<string name="pref_lock_timeout_15">after 15 Minutes</string>
<string name="pref_lock_timeout_30">after 30 Minutes</string>
<string name="pref_lock_timeout_60">after 1 Hour</string>
<string name="change_password">Change password</string> <string name="change_password">Change password</string>
<string name="current_password">Current password</string> <string name="current_password">Current password</string>
<string name="choose_new_password">New password</string> <string name="choose_new_password">New password</string>
......
...@@ -60,8 +60,17 @@ ...@@ -60,8 +60,17 @@
android:key="pref_key_lock" android:key="pref_key_lock"
android:persistent="false" android:persistent="false"
android:summary="@string/pref_lock_summary" android:summary="@string/pref_lock_summary"
android:title="@string/pref_lock_title" android:title="@string/pref_lock_title"/>
android:enabled="false"/>
<ListPreference
android:defaultValue="@string/pref_lock_timeout_value_default"
android:dependency="pref_key_lock"
android:entries="@array/pref_key_lock_timeout_entries"
android:entryValues="@array/pref_key_lock_timeout_values"
android:key="pref_key_lock_timeout"
android:persistent="false"
android:summary="@string/pref_lock_timeout_summary"
android:title="@string/pref_lock_timeout_title"/>
<Preference <Preference
android:key="pref_key_change_password" android:key="pref_key_change_password"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment