Add a system to improve app foreground observation.

There was previously a crash that occurred when multiple threads tried to use ProcessLifecycleOwner, and this will hopefully resolve that.
fork-5.53.8
Greyson Parrelli 2021-02-08 15:37:45 -05:00 zatwierdzone przez GitHub
rodzic a160af2d11
commit 3bdf2e7e2c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
13 zmienionych plików z 147 dodań i 49 usunięć

Wyświetl plik

@ -23,9 +23,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import androidx.multidex.MultiDexApplication;
import com.google.android.gms.security.ProviderInstaller;
@ -69,6 +66,7 @@ import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
@ -94,7 +92,7 @@ import java.util.concurrent.TimeUnit;
*
* @author Moxie Marlinspike
*/
public class ApplicationContext extends MultiDexApplication implements DefaultLifecycleObserver {
public class ApplicationContext extends MultiDexApplication implements AppForegroundObserver.Listener {
private static final String TAG = ApplicationContext.class.getSimpleName();
@ -102,8 +100,6 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
private ViewOnceMessageManager viewOnceMessageManager;
private PersistentLogger persistentLogger;
private volatile boolean isAppVisible;
public static ApplicationContext getInstance(Context context) {
return (ApplicationContext)context.getApplicationContext();
}
@ -133,7 +129,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
.addBlocking("app-migrations", this::initializeApplicationMigrations)
.addBlocking("ring-rtc", this::initializeRingRtc)
.addBlocking("mark-registration", () -> RegistrationUtil.maybeMarkRegistrationComplete(this))
.addBlocking("lifecycle-observer", () -> ProcessLifecycleOwner.get().getLifecycle().addObserver(this))
.addBlocking("lifecycle-observer", () -> ApplicationDependencies.getAppForegroundObserver().addListener(this))
.addBlocking("message-retriever", this::initializeMessageRetrieval)
.addBlocking("dynamic-theme", () -> DynamicTheme.setDefaultDayNightMode(this))
.addBlocking("vector-compat", () -> {
@ -164,16 +160,13 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
.addPostRender(() -> NotificationChannels.create(this))
.execute();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");
Tracer.getInstance().end("Application#onCreate()");
}
@Override
public void onStart(@NonNull LifecycleOwner owner) {
public void onForeground() {
long startTime = System.currentTimeMillis();
isAppVisible = true;
Log.i(TAG, "App is now visible.");
ApplicationDependencies.getFrameRateTracker().begin();
@ -194,8 +187,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
isAppVisible = false;
public void onBackground() {
Log.i(TAG, "App is no longer visible.");
KeyCachingService.onAppBackgrounded(this);
ApplicationDependencies.getMessageNotifier().clearVisibleThread();
@ -214,10 +206,6 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
return viewOnceMessageManager;
}
public boolean isAppVisible() {
return isAppVisible;
}
public PersistentLogger getPersistentLogger() {
return persistentLogger;
}

Wyświetl plik

@ -91,8 +91,8 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
@Override
public void onMasterSecretCleared() {
Log.d(TAG, "onMasterSecretCleared()");
if (ApplicationContext.getInstance(this).isAppVisible()) routeApplicationState(true);
else finish();
if (ApplicationDependencies.getAppForegroundObserver().isForegrounded()) routeApplicationState(true);
else finish();
}
protected <T extends Fragment> T initFragment(@IdRes int target,

Wyświetl plik

@ -56,7 +56,6 @@ import androidx.appcompat.widget.TooltipCompat;
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import androidx.lifecycle.ViewModelProviders;
@ -116,6 +115,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.AvatarUtil;
import org.thoughtcrime.securesms.util.PlayStoreUtil;
@ -175,7 +175,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
private Stub<ViewGroup> megaphoneContainer;
private SnapToTopDataObserver snapToTopDataObserver;
private Drawable archiveDrawable;
private LifecycleObserver visibilityLifecycleObserver;
private AppForegroundObserver.Listener appForegroundObserver;
private Stopwatch startupStopwatch;
@ -277,7 +277,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
public void onStart() {
super.onStart();
ConversationFragment.prepare(requireContext());
ProcessLifecycleOwner.get().getLifecycle().addObserver(visibilityLifecycleObserver);
ApplicationDependencies.getAppForegroundObserver().addListener(appForegroundObserver);
}
@Override
@ -292,7 +292,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@Override
public void onStop() {
super.onStop();
ProcessLifecycleOwner.get().getLifecycle().removeObserver(visibilityLifecycleObserver);
ApplicationDependencies.getAppForegroundObserver().removeListener(appForegroundObserver);
}
@Override
@ -554,11 +554,14 @@ public class ConversationListFragment extends MainFragment implements ActionMode
viewModel.hasNoConversations().observe(getViewLifecycleOwner(), this::updateEmptyState);
viewModel.getPipeState().observe(getViewLifecycleOwner(), this::updateProxyStatus);
visibilityLifecycleObserver = new DefaultLifecycleObserver() {
appForegroundObserver = new AppForegroundObserver.Listener() {
@Override
public void onStart(@NonNull LifecycleOwner owner) {
public void onForeground() {
viewModel.onVisible();
}
@Override
public void onBackground() { }
};
}

Wyświetl plik

@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.recipients.LiveRecipientCache;
import org.thoughtcrime.securesms.service.TrimThreadsByDateManager;
import org.thoughtcrime.securesms.shakereport.ShakeToReport;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.EarlyMessageCache;
import org.thoughtcrime.securesms.util.FrameRateTracker;
import org.thoughtcrime.securesms.util.Hex;
@ -49,9 +50,10 @@ public class ApplicationDependencies {
private static final Object FRAME_RATE_TRACKER_LOCK = new Object();
private static final Object JOB_MANAGER_LOCK = new Object();
private static Application application;
private static Provider provider;
private static MessageNotifier messageNotifier;
private static Application application;
private static Provider provider;
private static MessageNotifier messageNotifier;
private static AppForegroundObserver appForegroundObserver;
private static volatile SignalServiceAccountManager accountManager;
private static volatile SignalServiceMessageSender messageSender;
@ -80,9 +82,12 @@ public class ApplicationDependencies {
throw new IllegalStateException("Already initialized!");
}
ApplicationDependencies.application = application;
ApplicationDependencies.provider = provider;
ApplicationDependencies.messageNotifier = provider.provideMessageNotifier();
ApplicationDependencies.application = application;
ApplicationDependencies.provider = provider;
ApplicationDependencies.messageNotifier = provider.provideMessageNotifier();
ApplicationDependencies.appForegroundObserver = provider.provideAppForegroundObserver();
ApplicationDependencies.appForegroundObserver.begin();
}
}
@ -378,6 +383,11 @@ public class ApplicationDependencies {
return shakeToReport;
}
public static @NonNull AppForegroundObserver getAppForegroundObserver() {
return appForegroundObserver;
}
public interface Provider {
@NonNull PipeConnectivityListener providePipeListener();
@NonNull GroupsV2Operations provideGroupsV2Operations();
@ -399,5 +409,6 @@ public class ApplicationDependencies {
@NonNull TypingStatusSender provideTypingStatusSender();
@NonNull DatabaseObserver provideDatabaseObserver();
@NonNull ShakeToReport provideShakeToReport();
@NonNull AppForegroundObserver provideAppForegroundObserver();
}
}

Wyświetl plik

@ -51,6 +51,7 @@ import org.thoughtcrime.securesms.recipients.LiveRecipientCache;
import org.thoughtcrime.securesms.service.TrimThreadsByDateManager;
import org.thoughtcrime.securesms.shakereport.ShakeToReport;
import org.thoughtcrime.securesms.util.AlarmSleepTimer;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.ByteUnit;
import org.thoughtcrime.securesms.util.EarlyMessageCache;
import org.thoughtcrime.securesms.util.FeatureFlags;
@ -224,6 +225,11 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
return new ShakeToReport(context);
}
@Override
public @NonNull AppForegroundObserver provideAppForegroundObserver() {
return new AppForegroundObserver();
}
private static class DynamicCredentialsProvider implements CredentialsProvider {
private final Context context;

Wyświetl plik

@ -166,7 +166,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
private void generateFullContactUpdate()
throws IOException, UntrustedIdentityException, NetworkException
{
boolean isAppVisible = ApplicationContext.getInstance(context).isAppVisible();
boolean isAppVisible = ApplicationDependencies.getAppForegroundObserver().isForegrounded();
long timeSinceLastSync = System.currentTimeMillis() - TextSecurePreferences.getLastFullContactSyncTime(context);
Log.d(TAG, "Requesting a full contact sync. forced = " + forceSync + ", appVisible = " + isAppVisible + ", timeSinceLastSync = " + timeSinceLastSync + " ms");

Wyświetl plik

@ -107,7 +107,7 @@ public class BackgroundMessageRetriever {
* care of it.
*/
public static boolean shouldIgnoreFetch(@NonNull Context context) {
return ApplicationContext.getInstance(context).isAppVisible() &&
return ApplicationDependencies.getAppForegroundObserver().isForegrounded() &&
!ApplicationDependencies.getSignalServiceNetworkAccess().isCensored(context);
}

Wyświetl plik

@ -13,9 +13,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
@ -27,6 +24,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.messages.IncomingMessageProcessor.Processor;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.InvalidVersionException;
import org.whispersystems.libsignal.util.guava.Optional;
@ -71,14 +69,14 @@ public class IncomingMessageObserver {
ContextCompat.startForegroundService(context, new Intent(context, ForegroundService.class));
}
ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() {
ApplicationDependencies.getAppForegroundObserver().addListener(new AppForegroundObserver.Listener() {
@Override
public void onStart(@NonNull LifecycleOwner owner) {
public void onForeground() {
onAppForegrounded();
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
public void onBackground() {
onAppBackgrounded();
}
});

Wyświetl plik

@ -226,7 +226,7 @@ public class KeyCachingService extends Service {
}
private static void startTimeoutIfAppropriate(@NonNull Context context) {
boolean appVisible = ApplicationContext.getInstance(context).isAppVisible();
boolean appVisible = ApplicationDependencies.getAppForegroundObserver().isForegrounded();
boolean secretSet = KeyCachingService.masterSecret != null;
boolean timeoutEnabled = TextSecurePreferences.isPassphraseTimeoutEnabled(context);

Wyświetl plik

@ -446,7 +446,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
}
public void setCallInProgressNotification(int type, @NonNull Recipient recipient) {
startForeground(CallNotificationBuilder.getNotificationId(getApplicationContext(), type),
startForeground(CallNotificationBuilder.getNotificationId(type),
CallNotificationBuilder.getCallInProgressNotification(this, type, recipient));
}
@ -480,7 +480,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
}
public void startCallCardActivityIfPossible() {
if (Build.VERSION.SDK_INT >= 29 && !ApplicationContext.getInstance(getApplicationContext()).isAppVisible()) {
if (Build.VERSION.SDK_INT >= 29 && !ApplicationDependencies.getAppForegroundObserver().isForegrounded()) {
return;
}

Wyświetl plik

@ -13,6 +13,7 @@ import org.signal.core.util.logging.Log;
import org.signal.core.util.tracing.Tracer;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogRepository;
import org.thoughtcrime.securesms.sharing.ShareIntents;
import org.thoughtcrime.securesms.util.FeatureFlags;
@ -131,7 +132,7 @@ public final class ShakeToReport implements ShakeDetector.Listener {
}
private void enableIfVisible() {
if (ApplicationContext.getInstance(application).isAppVisible()) {
if (ApplicationDependencies.getAppForegroundObserver().isForegrounded()) {
enable();
}
}

Wyświetl plik

@ -0,0 +1,90 @@
package org.thoughtcrime.securesms.util;
import androidx.annotation.AnyThread;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* A wrapper around {@link ProcessLifecycleOwner} that allows for safely adding/removing observers
* on multiple threads.
*/
public final class AppForegroundObserver {
private final Set<Listener> listeners = new CopyOnWriteArraySet<>();
private volatile Boolean isForegrounded = null;
@MainThread
public void begin() {
Util.assertMainThread();
ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() {
@Override
public void onStart(@NonNull LifecycleOwner owner) {
onForeground();
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
onBackground();
}
});
}
/**
* Adds a listener to be notified of when the app moves between the background and the foreground.
* To mimic the behavior of subscribing to {@link ProcessLifecycleOwner}, this listener will be
* immediately notified of the foreground state if we've experienced a foreground/background event
* already.
*/
@AnyThread
public void addListener(@NonNull Listener listener) {
listeners.add(listener);
if (isForegrounded != null) {
if (isForegrounded) {
listener.onForeground();
} else {
listener.onBackground();
}
}
}
@AnyThread
public void removeListener(@NonNull Listener listener) {
listeners.remove(listener);
}
public boolean isForegrounded() {
return isForegrounded;
}
@MainThread
private void onForeground() {
isForegrounded = true;
for (Listener listener : listeners) {
listener.onForeground();
}
}
@MainThread
private void onBackground() {
isForegrounded = false;
for (Listener listener : listeners) {
listener.onBackground();
}
}
public interface Listener {
void onForeground();
void onBackground();
}
}

Wyświetl plik

@ -14,6 +14,7 @@ import androidx.core.app.NotificationCompat;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.WebRtcCallActivity;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.WebRtcCallService;
@ -55,7 +56,7 @@ public class CallNotificationBuilder {
builder.addAction(getServiceNotificationAction(context, WebRtcCallService.ACTION_DENY_CALL, R.drawable.ic_close_grey600_32dp, R.string.NotificationBarManager__deny_call));
builder.addAction(getActivityNotificationAction(context, WebRtcCallActivity.ANSWER_ACTION, R.drawable.ic_phone_grey600_32dp, R.string.NotificationBarManager__answer_call));
if (callActivityRestricted(context)) {
if (callActivityRestricted()) {
builder.setFullScreenIntent(pendingIntent, true);
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
builder.setCategory(NotificationCompat.CATEGORY_CALL);
@ -71,8 +72,8 @@ public class CallNotificationBuilder {
return builder.build();
}
public static int getNotificationId(@NonNull Context context, int type) {
if (callActivityRestricted(context) && type == TYPE_INCOMING_RINGING) {
public static int getNotificationId(int type) {
if (callActivityRestricted() && type == TYPE_INCOMING_RINGING) {
return WEBRTC_NOTIFICATION_RINGING;
} else {
return WEBRTC_NOTIFICATION;
@ -85,7 +86,7 @@ public class CallNotificationBuilder {
}
private static @NonNull String getNotificationChannel(@NonNull Context context, int type) {
if (callActivityRestricted(context) && type == TYPE_INCOMING_RINGING) {
if (callActivityRestricted() && type == TYPE_INCOMING_RINGING) {
return NotificationChannels.CALLS;
} else {
return NotificationChannels.OTHER;
@ -112,7 +113,7 @@ public class CallNotificationBuilder {
return new NotificationCompat.Action(iconResId, context.getString(titleResId), pendingIntent);
}
private static boolean callActivityRestricted(@NonNull Context context) {
return Build.VERSION.SDK_INT >= 29 && !ApplicationContext.getInstance(context).isAppVisible();
private static boolean callActivityRestricted() {
return Build.VERSION.SDK_INT >= 29 && !ApplicationDependencies.getAppForegroundObserver().isForegrounded();
}
}