Fixing MainThreadInitializedObject

> Making SafeCloseable implementation mandatory, to prevent leaks during test and preview
> Removing getNoCreate method and defining executeIfCreated to avoid null pointer exceptions
> Fixing sandbox value leaking into main, by Checking sandbox against App context
> Converting sanbox to an interface instead a class

Bug: 335280439
Test: Presubmit
Flag: None
Change-Id: I951dcde871898e745ff6490a1c4f8fd1512888f5
This commit is contained in:
Sunny Goyal
2024-04-21 00:13:35 -07:00
parent 1f40fa0e7f
commit 10fa016352
35 changed files with 233 additions and 245 deletions
@@ -326,8 +326,12 @@ public class QuickstepModelDelegate extends ModelDelegate {
super.destroy();
mActive = false;
StatsLogCompatManager.LOGS_CONSUMER.remove(mAppEventProducer);
if (mIsPrimaryInstance) {
mStatsManager.clearPullAtomCallback(SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT);
if (mIsPrimaryInstance && mStatsManager != null) {
try {
mStatsManager.clearPullAtomCallback(SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT);
} catch (RuntimeException e) {
Log.e(TAG, "Failed to unregister snapshot logging callback with StatsManager", e);
}
}
destroyPredictors();
}
@@ -32,7 +32,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -48,9 +47,10 @@ import com.android.launcher3.R;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.RemoteActionShortcut;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.BgObjectWithLooper;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.views.ActivityContext;
@@ -61,7 +61,7 @@ import java.util.Map;
/**
* Data model for digital wellbeing status of apps.
*/
public final class WellbeingModel extends BgObjectWithLooper {
public final class WellbeingModel implements SafeCloseable {
private static final String TAG = "WellbeingModel";
private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
private static final boolean DEBUG = false;
@@ -81,8 +81,12 @@ public final class WellbeingModel extends BgObjectWithLooper {
private final Context mContext;
private final String mWellbeingProviderPkg;
private Handler mWorkerHandler;
private ContentObserver mContentObserver;
private final Handler mWorkerHandler;
private final ContentObserver mContentObserver;
private final SimpleBroadcastReceiver mWellbeingAppChangeReceiver =
new SimpleBroadcastReceiver(t -> restartObserver());
private final SimpleBroadcastReceiver mAppAddRemoveReceiver =
new SimpleBroadcastReceiver(this::onAppPackageChanged);
private final Object mModelLock = new Object();
// Maps the action Id to the corresponding RemoteAction
@@ -94,16 +98,23 @@ public final class WellbeingModel extends BgObjectWithLooper {
private WellbeingModel(final Context context) {
mContext = context;
mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
initializeInBackground("WellbeingHandler");
mWorkerHandler = new Handler(TextUtils.isEmpty(mWellbeingProviderPkg)
? Executors.UI_HELPER_EXECUTOR.getLooper()
: Executors.getPackageExecutor(mWellbeingProviderPkg).getLooper());
mContentObserver = new ContentObserver(mWorkerHandler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateAllPackages();
}
};
mWorkerHandler.post(this::initializeInBackground);
}
@Override
protected void onInitialized(Looper looper) {
mWorkerHandler = new Handler(looper);
mContentObserver = newContentObserver(mWorkerHandler, this::onWellbeingUriChanged);
private void initializeInBackground() {
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
mContext.registerReceiver(
new SimpleBroadcastReceiver(t -> restartObserver()),
mWellbeingAppChangeReceiver,
getPackageFilter(mWellbeingProviderPkg,
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
@@ -113,17 +124,21 @@ public final class WellbeingModel extends BgObjectWithLooper {
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
mContext.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
filter, null, mWorkerHandler);
mContext.registerReceiver(mAppAddRemoveReceiver, filter, null, mWorkerHandler);
restartObserver();
}
}
@WorkerThread
private void onWellbeingUriChanged(Uri uri) {
Preconditions.assertNonUiThread();
updateAllPackages();
@Override
public void close() {
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
mWorkerHandler.post(() -> {
mWellbeingAppChangeReceiver.unregisterReceiverSafely(mContext);
mAppAddRemoveReceiver.unregisterReceiverSafely(mContext);
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
});
}
}
public void setInTest(boolean inTest) {
@@ -39,6 +39,7 @@ import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SafeCloseable;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -50,7 +51,7 @@ import java.util.ArrayList;
/**
* Helper class for transforming touch events
*/
public class RotationTouchHelper implements DisplayInfoChangeListener {
public class RotationTouchHelper implements DisplayInfoChangeListener, SafeCloseable {
public static final MainThreadInitializedObject<RotationTouchHelper> INSTANCE =
new MainThreadInitializedObject<>(RotationTouchHelper::new);
@@ -197,6 +198,11 @@ public class RotationTouchHelper implements DisplayInfoChangeListener {
mOnDestroyActions.add(action);
}
@Override
public void close() {
destroy();
}
/**
* Cleans up all the registered listeners and receivers.
*/
@@ -24,21 +24,29 @@ import android.view.MotionEvent;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
public class SimpleOrientationTouchTransformer implements
DisplayController.DisplayInfoChangeListener {
DisplayController.DisplayInfoChangeListener, SafeCloseable {
public static final MainThreadInitializedObject<SimpleOrientationTouchTransformer> INSTANCE =
new MainThreadInitializedObject<>(SimpleOrientationTouchTransformer::new);
private final Context mContext;
private OrientationRectF mOrientationRectF;
public SimpleOrientationTouchTransformer(Context context) {
mContext = context;
DisplayController.INSTANCE.get(context).addChangeListener(this);
onDisplayInfoChanged(context, DisplayController.INSTANCE.get(context).getInfo(),
CHANGE_ALL);
}
@Override
public void close() {
DisplayController.INSTANCE.get(mContext).removeChangeListener(this);
}
@Override
public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
if ((flags & (CHANGE_ROTATION | CHANGE_ACTIVE_SCREEN)) == 0) {
@@ -65,6 +65,7 @@ import com.android.internal.util.ScreenshotRequest;
import com.android.internal.view.AppearanceRegion;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AssistUtils;
import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider;
@@ -108,7 +109,7 @@ import java.util.List;
/**
* Holds the reference to SystemUI.
*/
public class SystemUiProxy implements ISystemUiProxy, NavHandle {
public class SystemUiProxy implements ISystemUiProxy, NavHandle, SafeCloseable {
private static final String TAG = SystemUiProxy.class.getSimpleName();
public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
@@ -198,6 +199,9 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
? new ProxyUnfoldTransitionProvider() : null;
}
@Override
public void close() { }
@Override
public void onBackPressed() {
if (mSystemUiProxy != null) {
@@ -59,6 +59,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
&& SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
private final Context mCtx;
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
private RecentsAnimationTargets mTargets;
@@ -66,7 +67,6 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
private GestureState mLastGestureState;
private RemoteAnimationTarget[] mLastAppearedTaskTargets;
private Runnable mLiveTileCleanUpHandler;
private Context mCtx;
private boolean mRecentsAnimationStartPending = false;
private boolean mShouldIgnoreMotionEvents = false;
@@ -329,7 +329,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
options.setTransientLaunch();
}
options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
mRecentsAnimationStartPending = SystemUiProxy.INSTANCE.getNoCreate()
mRecentsAnimationStartPending = SystemUiProxy.INSTANCE.get(mCtx)
.startRecentsActivity(intent, options, mCallbacks);
if (enableHandleDelayedGestureCallbacks()) {
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
@@ -34,6 +34,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitStageInfo;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
@@ -57,7 +58,8 @@ import java.util.List;
* This class tracked the top-most task and some 'approximate' task history to allow faster
* system state estimation during touch interaction
*/
public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskStackChangeListener {
public class TopTaskTracker extends ISplitScreenListener.Stub
implements TaskStackChangeListener, SafeCloseable {
public static MainThreadInitializedObject<TopTaskTracker> INSTANCE =
new MainThreadInitializedObject<>(TopTaskTracker::new);
@@ -67,12 +69,13 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
// Ordered list with first item being the most recent task.
private final LinkedList<RunningTaskInfo> mOrderedTaskList = new LinkedList<>();
private final Context mContext;
private final SplitStageInfo mMainStagePosition = new SplitStageInfo();
private final SplitStageInfo mSideStagePosition = new SplitStageInfo();
private int mPinnedTaskId = INVALID_TASK_ID;
private TopTaskTracker(Context context) {
mContext = context;
mMainStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_MAIN;
mSideStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_SIDE;
@@ -80,6 +83,12 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
SystemUiProxy.INSTANCE.get(context).registerSplitScreenListener(this);
}
@Override
public void close() {
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this);
SystemUiProxy.INSTANCE.get(mContext).unregisterSplitScreenListener(this);
}
@Override
public void onTaskRemoved(int taskId) {
mOrderedTaskList.removeIf(rto -> rto.taskId == taskId);
@@ -347,7 +347,6 @@ public class StatsLogCompatManager extends StatsLogManager {
event.getId() + "";
Log.d(TAG, name);
}
LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
if (mSlice == null && mSliceItem != null) {
mSlice = LauncherAtom.Slice.newBuilder().setUri(
@@ -369,15 +368,10 @@ public class StatsLogCompatManager extends StatsLogManager {
return;
}
if (mItemInfo.container < 0 || appState == null) {
// Write log on the model thread so that logs do not go out of order
// (for eg: drop comes after drag)
Executors.MODEL_EXECUTOR.execute(
() -> write(event, applyOverwrites(mItemInfo.buildProto())));
} else {
if (mItemInfo.container < 0 || !LauncherAppState.INSTANCE.executeIfCreated(app -> {
// Item is inside a collection, fetch collection info in a BG thread
// and then write to StatsLog.
appState.getModel().enqueueModelUpdateTask(
app.getModel().enqueueModelUpdateTask(
new BaseModelUpdateTask() {
@Override
public void execute(@NonNull final LauncherAppState app,
@@ -388,6 +382,11 @@ public class StatsLogCompatManager extends StatsLogManager {
write(event, applyOverwrites(mItemInfo.buildProto(collectionInfo)));
}
});
})) {
// Write log on the model thread so that logs do not go out of order
// (for eg: drop comes after drag)
Executors.MODEL_EXECUTOR.execute(
() -> write(event, applyOverwrites(mItemInfo.buildProto())));
}
}
@@ -316,7 +316,7 @@ public class AppPairsController {
itemInfos.stream().map(ItemInfo::getComponentKey).toList();
// Use TopTaskTracker to find the currently running app (or apps)
TopTaskTracker topTaskTracker = getTopTaskTracker(context);
TopTaskTracker topTaskTracker = getTopTaskTracker();
// getRunningSplitTasksIds() will return a pair of ids if we are currently running a
// split pair, or an empty array with zero length if we are running a single app.
@@ -489,7 +489,7 @@ public class AppPairsController {
* Gets the TopTaskTracker, which is a cached record of the top running Task.
*/
@VisibleForTesting
public TopTaskTracker getTopTaskTracker(Context context) {
return TopTaskTracker.INSTANCE.get(context);
public TopTaskTracker getTopTaskTracker() {
return TopTaskTracker.INSTANCE.get(mContext);
}
}
@@ -63,7 +63,7 @@ public class SplitToWorkspaceController {
SplitSelectStateController controller) {
mLauncher = launcher;
mController = controller;
mIconCache = LauncherAppState.getInstanceNoCreate().getIconCache();
mIconCache = LauncherAppState.getInstance(launcher).getIconCache();
mHalfDividerSize = mLauncher.getResources().getDimensionPixelSize(
R.dimen.multi_window_task_divider_size) / 2;
}
@@ -22,6 +22,8 @@ import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
import static com.android.launcher3.BaseActivity.EVENT_RESUMED;
import static com.android.launcher3.BaseActivity.EVENT_STOPPED;
import android.content.Context;
import androidx.annotation.NonNull;
import com.android.quickstep.RecentsModel;
@@ -45,6 +47,12 @@ public class TaskRemovedDuringLaunchListener {
private final Runnable mUnregisterCallback = this::unregister;
private final Runnable mResumeCallback = this::checkTaskLaunchFailed;
private final Context mContext;
public TaskRemovedDuringLaunchListener(Context context) {
mContext = context;
}
/**
* Registers a failure listener callback if it detects a scenario in which an app launch
* failed before the transition finished.
@@ -88,7 +96,7 @@ public class TaskRemovedDuringLaunchListener {
if (mLaunchedTaskId != INVALID_TASK_ID) {
final int launchedTaskId = mLaunchedTaskId;
final Runnable taskLaunchFailedCallback = mTaskLaunchFailedCallback;
RecentsModel.INSTANCE.getNoCreate().isTaskRemoved(mLaunchedTaskId, (taskRemoved) -> {
RecentsModel.INSTANCE.get(mContext).isTaskRemoved(mLaunchedTaskId, (taskRemoved) -> {
if (taskRemoved) {
ActiveGestureLog.INSTANCE.addLog(
new ActiveGestureLog.CompoundString("Launch failed, task (id=")
@@ -93,6 +93,7 @@ import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.CancellableTask;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.TraceHelper;
@@ -118,6 +119,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import kotlin.Unit;
import java.lang.annotation.Retention;
import java.util.Arrays;
import java.util.Collections;
@@ -126,8 +129,6 @@ import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;
import kotlin.Unit;
/**
* A task in the Recents view.
*/
@@ -922,8 +923,8 @@ public class TaskView extends FrameLayout implements Reusable {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
final TaskRemovedDuringLaunchListener
failureListener = new TaskRemovedDuringLaunchListener();
TaskRemovedDuringLaunchListener failureListener = new TaskRemovedDuringLaunchListener(
getContext().getApplicationContext());
if (isQuickswitch) {
// We only listen for failures to launch in quickswitch because the during this
// gesture launcher is in the background state, vs other launches which are in
@@ -950,12 +951,8 @@ public class TaskView extends FrameLayout implements Reusable {
// Indicate success once the system has indicated that the transition has started
ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(getContext(), 0, 0,
MAIN_EXECUTOR.getHandler(),
elapsedRealTime -> {
callback.accept(true);
},
elapsedRealTime -> {
failureListener.onTransitionFinished();
});
elapsedRealTime -> callback.accept(true),
elapsedRealTime -> failureListener.onTransitionFinished());
opts.setLaunchDisplayId(
getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
if (isQuickswitch) {
@@ -1857,7 +1854,7 @@ public class TaskView extends FrameLayout implements Reusable {
/**
* We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
*/
public static class FullscreenDrawParams {
public static class FullscreenDrawParams implements SafeCloseable {
private float mCornerRadius;
private float mWindowCornerRadius;
@@ -1892,6 +1889,9 @@ public class TaskView extends FrameLayout implements Reusable {
Utilities.mapRange(fullscreenProgress, mCornerRadius, mWindowCornerRadius)
/ parentScale / taskViewScale;
}
@Override
public void close() { }
}
public class TaskIdAttributeContainer {