Skip to content

Commit

Permalink
Listen for screen events to fix auto-lock inactivity timer
Browse files Browse the repository at this point in the history
It fixes the issue that incoming calls reset the inactivity timer
and could prevent the automatic lockdown from triggering, even if
the device screen is locked.
  • Loading branch information
valldrac committed Apr 3, 2020
1 parent 80251db commit dd8014e
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 39 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@ All notable changes to Signal will be documented in this file.

## [Unreleased]

### Changed

- Auto-lock is not trigger due to app inactivity anymore, but the last time the device screen was unlocked.

### Removed

- Flipper build type.

### Fixed

- Incoming calls reset the inactivity timer.

### Merged

- Signal v4.58.4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.LocalBackupListener;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.service.WipeMemoryService;
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
Expand Down Expand Up @@ -180,6 +181,8 @@ public void onUnlock() {
public void onLock() {
Log.i(TAG, "onLock()");

stopService(new Intent(this, WebRtcCallService.class));

finalizeRevealableMessageManager();
finalizeExpiringMessageManager();
finalizeMessageRetrieval();
Expand All @@ -196,7 +199,6 @@ public void onLock() {
public void onStart(@NonNull LifecycleOwner owner) {
isAppVisible = true;
Log.i(TAG, "App is now visible.");
KeyCachingService.onAppForegrounded(this);
if (!KeyCachingService.isLocked()) {
onStartUnlock();
}
Expand All @@ -214,7 +216,6 @@ public void onStartUnlock() {
public void onStop(@NonNull LifecycleOwner owner) {
isAppVisible = false;
Log.i(TAG, "App is no longer visible.");
KeyCachingService.onAppBackgrounded(this);
if (!KeyCachingService.isLocked()) {
onStopUnlock();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ private CharSequence getSummaryForPassphraseLockTrigger(Set<String> triggers) {
}

return outValues.isEmpty() ? getResources().getString(R.string.preferences__none)
: TextUtils.join(", ", outValues);
: TextUtils.join(". ", outValues);
}

private class PassphraseLockListener implements Preference.OnPreferenceChangeListener {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.SystemClock;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;

import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.MainActivity;
import org.thoughtcrime.securesms.logging.Log;
Expand Down Expand Up @@ -69,6 +69,8 @@ public class KeyCachingService extends Service {

private final IBinder binder = new KeySetBinder();

private boolean pendingAlarm;

private static MasterSecret masterSecret;

private static volatile boolean locking;
Expand Down Expand Up @@ -99,14 +101,6 @@ public static synchronized void clearMasterSecret() {
locking = false;
}

public static void onAppForegrounded(@NonNull Context context) {
ServiceUtil.getAlarmManager(context).cancel(buildExpirationPendingIntent(context));
}

public static void onAppBackgrounded(@NonNull Context context) {
startTimeoutIfAppropriate(context);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) return START_NOT_STICKY;
Expand All @@ -127,14 +121,16 @@ public int onStartCommand(Intent intent, int flags, int startId) {

@Override
public void onCreate() {
Log.i(TAG, "onCreate()");
super.onCreate();
Log.d(TAG, "onCreate");
registerScreenReceiver();
}

@Override
public void onDestroy() {
super.onDestroy();
Log.w(TAG, "KCS Is Being Destroyed!");
unregisterScreenReceiver();
if (locking) clearMasterSecret();
}

Expand All @@ -151,10 +147,13 @@ public void onTaskRemoved(Intent rootIntent) {

@SuppressLint("StaticFieldLeak")
public void handleCacheKey() {
Log.i(TAG, "handleCacheKey()");
Log.i(TAG, "handleCacheKey");

foregroundService();
startTimeoutIfAppropriate(this);

if (ServiceUtil.getKeyguardManager(this).isKeyguardLocked()) {
startTimeoutIfAppropriate();
}

new AsyncTask<Void, Void, Void>() {
@Override
Expand All @@ -169,7 +168,9 @@ protected Void doInBackground(Void... params) {

@SuppressLint("StaticFieldLeak")
private void handleClearKey() {
Log.i(TAG, "handleClearKey()");
Log.i(TAG, "handleClearKey");

pendingAlarm = false;

if (ApplicationMigrations.isUpdate(this)) {
Log.w(TAG, "Cannot clear key during update.");
Expand All @@ -194,30 +195,43 @@ private void handleLocaleChanged() {
foregroundService();
}

private static void startTimeoutIfAppropriate(@NonNull Context context) {
if (!ApplicationContext.getInstance(context).isAppVisible()
&& !KeyCachingService.isLocked()
&& TextSecurePreferences.isPassphraseLockEnabled(context)
&& TextSecurePreferences.getPassphraseLockTrigger(context).isTimeoutEnabled()) {
long lockTimeoutSeconds = TextSecurePreferences.getPassphraseLockTimeout(context);
if (lockTimeoutSeconds > 0) {
scheduleTimeout(context, lockTimeoutSeconds, buildExpirationPendingIntent(context));
}
private void startTimeoutIfAppropriate() {
long lockTimeoutSeconds = 0;
if (!KeyCachingService.isLocked()
&& TextSecurePreferences.isPassphraseLockEnabled(this)
&& TextSecurePreferences.getPassphraseLockTrigger(this).isTimeoutEnabled()) {
lockTimeoutSeconds = TextSecurePreferences.getPassphraseLockTimeout(this);
}
if (lockTimeoutSeconds > 0) {
scheduleTimeout(lockTimeoutSeconds);
} else {
cancelTimeout();
}
}

private static void scheduleTimeout(@NonNull Context context, long timeoutSeconds, PendingIntent operation) {
private void scheduleTimeout(long timeoutSeconds) {
if (pendingAlarm) return;

Log.i(TAG, "Starting timeout: " + timeoutSeconds + " s.");

long at = SystemClock.elapsedRealtime() + TimeUnit.SECONDS.toMillis(timeoutSeconds);

AlarmManager alarmManager = ServiceUtil.getAlarmManager(context);
alarmManager.cancel(operation);
AlarmManager alarmManager = ServiceUtil.getAlarmManager(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, at, operation);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, at, buildExpirationIntent());
} else {
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, at, operation);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, at, buildExpirationIntent());
}

pendingAlarm = true;
}

private void cancelTimeout() {
AlarmManager alarmManager = ServiceUtil.getAlarmManager(this);
alarmManager.cancel(buildExpirationIntent());
pendingAlarm = false;

Log.i(TAG, "Timeout canceled");
}

private void foregroundService() {
Expand Down Expand Up @@ -260,14 +274,15 @@ private PendingIntent buildLockIntent() {

private PendingIntent buildLaunchIntent() {
// TODO [greyson] Navigation
Intent intent = new Intent(this, MainActivity.class);
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
return PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
}

private static PendingIntent buildExpirationPendingIntent(@NonNull Context context) {
Intent expirationIntent = new Intent(PASSPHRASE_EXPIRED_EVENT, null, context, KeyCachingService.class);
return PendingIntent.getService(context, 0, expirationIntent, 0);
private PendingIntent buildExpirationIntent() {
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(PASSPHRASE_EXPIRED_EVENT);
return PendingIntent.getService(getApplicationContext(), 0, intent, 0);
}

@Override
Expand All @@ -280,4 +295,25 @@ public KeyCachingService getService() {
return KeyCachingService.this;
}
}

private void registerScreenReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);

registerReceiver(screenReceiver, filter);
}

private void unregisterScreenReceiver() {
unregisterReceiver(screenReceiver);
}

private BroadcastReceiver screenReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "onReceive, " + action);
if (Intent.ACTION_SCREEN_OFF .equals(action)) startTimeoutIfAppropriate();
else if (Intent.ACTION_USER_PRESENT.equals(action)) cancelTimeout();
}
};
}
6 changes: 3 additions & 3 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1471,7 +1471,7 @@
<string name="arrays__high">High</string>
<string name="arrays__max">Max</string>

<string name="arrays__on_inactivity_timeout">On inactivity timeout</string>
<string name="arrays__on_inactivity_timeout">If you go a certain time without unlocking your device</string>

<!-- plurals.xml -->
<plurals name="hours_ago">
Expand All @@ -1497,13 +1497,13 @@
<string name="preferences__lock_signal_and_message_notifications_with_a_passphrase">Lock screen and notifications with a passphrase</string>
<string name="preferences__passphrase_lock">Database encryption</string>
<string name="preferences__protect_molly_database_with_a_passphrase">Protect Molly\'s database with a passphrase</string>
<string name="preferences__automatic_database_lockdown">Automatic database lockdown</string>
<string name="preferences__automatic_lockdown">Automatic lockdown</string>
<string name="preferences__passphrase_changed">Passphrase changed</string>
<string name="preferences__screen_security">Screen security</string>
<string name="preferences__disable_screen_security_to_allow_screen_shots">Block screenshots in the recents list and inside the app</string>
<string name="preferences__auto_lock_signal_after_a_specified_time_interval_of_inactivity">Auto-lock Signal after a specified time interval of inactivity</string>
<string name="preferences__inactivity_timeout_passphrase">Inactivity timeout passphrase</string>
<string name="preferences__inactivity_timeout_interval">Inactivity timeout interval</string>
<string name="preferences__inactivity_timeout_interval">Device unlock timeout</string>
<string name="preferences__notifications">Notifications</string>
<string name="preferences__system_notification_settings">System notification settings</string>
<string name="preferences__led_color">LED color</string>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/xml/preferences_app_protection.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
android:dependency="pref_passphrase_lock"/>

<MultiSelectListPreference
android:title="Automatic lockdown"
android:title="@string/preferences__automatic_lockdown"
android:key="pref_passphrase_lock_trigger"
android:defaultValue="@array/pref_passphrase_lock_trigger_default"
android:persistent="true"
Expand Down

0 comments on commit dd8014e

Please sign in to comment.