Merge "Fix Toast memory leaks" into udc-dev

This commit is contained in:
Iavor-Valentin Iftime
2023-05-12 17:12:32 +00:00
committed by Android (Google) Code Review
2 changed files with 48 additions and 29 deletions

View File

@@ -50,6 +50,7 @@ import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -205,7 +206,7 @@ public class Toast {
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
tn.mNextView = new WeakReference<>(mNextView);
final boolean isUiContext = mContext.isUiContext();
final int displayId = mContext.getDisplayId();
@@ -622,7 +623,7 @@ public class Toast {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
View mView;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
View mNextView;
WeakReference<View> mNextView;
int mDuration;
WindowManager mWM;
@@ -632,7 +633,7 @@ public class Toast {
private final ToastPresenter mPresenter;
@GuardedBy("mCallbacks")
private final List<Callback> mCallbacks;
private final WeakReference<List<Callback>> mCallbacks;
/**
* Creates a {@link ITransientNotification} object.
@@ -649,7 +650,7 @@ public class Toast {
mParams = mPresenter.getLayoutParams();
mPackageName = packageName;
mToken = token;
mCallbacks = callbacks;
mCallbacks = new WeakReference<>(callbacks);
mHandler = new Handler(looper, null) {
@Override
@@ -685,7 +686,11 @@ public class Toast {
private List<Callback> getCallbacks() {
synchronized (mCallbacks) {
return new ArrayList<>(mCallbacks);
if (mCallbacks.get() != null) {
return new ArrayList<>(mCallbacks.get());
} else {
return new ArrayList<>();
}
}
}
@@ -721,13 +726,15 @@ public class Toast {
if (mHandler.hasMessages(CANCEL) || mHandler.hasMessages(HIDE)) {
return;
}
if (mView != mNextView) {
if (mNextView != null && mView != mNextView.get()) {
// remove the old view if necessary
handleHide();
mView = mNextView;
mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
mHorizontalMargin, mVerticalMargin,
new CallbackBinder(getCallbacks(), mHandler));
mView = mNextView.get();
if (mView != null) {
mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
mHorizontalMargin, mVerticalMargin,
new CallbackBinder(getCallbacks(), mHandler));
}
}
}

View File

@@ -41,6 +41,8 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import java.lang.ref.WeakReference;
/**
* Class responsible for toast presentation inside app's process and in system UI.
*
@@ -87,25 +89,33 @@ public class ToastPresenter {
return view;
}
private final Context mContext;
private final Resources mResources;
private final WindowManager mWindowManager;
private final IAccessibilityManager mAccessibilityManager;
private final WeakReference<WindowManager> mWindowManager;
private final WeakReference<AccessibilityManager> mAccessibilityManager;
private final INotificationManager mNotificationManager;
private final String mPackageName;
private final String mContextPackageName;
private final WindowManager.LayoutParams mParams;
@Nullable private View mView;
@Nullable private IBinder mToken;
public ToastPresenter(Context context, IAccessibilityManager accessibilityManager,
INotificationManager notificationManager, String packageName) {
mContext = context;
mResources = context.getResources();
mWindowManager = context.getSystemService(WindowManager.class);
mWindowManager = new WeakReference<>(context.getSystemService(WindowManager.class));
mNotificationManager = notificationManager;
mPackageName = packageName;
mAccessibilityManager = accessibilityManager;
mContextPackageName = context.getPackageName();
mParams = createLayoutParams();
// We obtain AccessibilityManager manually via its constructor instead of using method
// AccessibilityManager.getInstance() for 2 reasons:
// 1. We want to be able to inject IAccessibilityManager in tests to verify behavior.
// 2. getInstance() caches the instance for the process even if we pass a different
// context to it. This is problematic for multi-user because callers can pass a context
// created via Context.createContextAsUser().
mAccessibilityManager = new WeakReference<>(
new AccessibilityManager(context, accessibilityManager, context.getUserId()));
}
public String getPackageName() {
@@ -173,7 +183,7 @@ public class ToastPresenter {
params.y = yOffset;
params.horizontalMargin = horizontalMargin;
params.verticalMargin = verticalMargin;
params.packageName = mContext.getPackageName();
params.packageName = mContextPackageName;
params.hideTimeoutMilliseconds =
(duration == Toast.LENGTH_LONG) ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
params.token = windowToken;
@@ -270,8 +280,9 @@ public class ToastPresenter {
public void hide(@Nullable ITransientNotificationCallback callback) {
checkState(mView != null, "No toast to hide.");
if (mView.getParent() != null) {
mWindowManager.removeViewImmediate(mView);
final WindowManager windowManager = mWindowManager.get();
if (mView.getParent() != null && windowManager != null) {
windowManager.removeViewImmediate(mView);
}
try {
mNotificationManager.finishToken(mPackageName, mToken);
@@ -295,14 +306,11 @@ public class ToastPresenter {
* enabled.
*/
public void trySendAccessibilityEvent(View view, String packageName) {
// We obtain AccessibilityManager manually via its constructor instead of using method
// AccessibilityManager.getInstance() for 2 reasons:
// 1. We want to be able to inject IAccessibilityManager in tests to verify behavior.
// 2. getInstance() caches the instance for the process even if we pass a different
// context to it. This is problematic for multi-user because callers can pass a context
// created via Context.createContextAsUser().
final AccessibilityManager accessibilityManager =
new AccessibilityManager(mContext, mAccessibilityManager, mContext.getUserId());
final AccessibilityManager accessibilityManager = mAccessibilityManager.get();
if (accessibilityManager == null) {
return;
}
if (!accessibilityManager.isEnabled()) {
accessibilityManager.removeClient();
return;
@@ -320,11 +328,15 @@ public class ToastPresenter {
}
private void addToastView() {
final WindowManager windowManager = mWindowManager.get();
if (windowManager == null) {
return;
}
if (mView.getParent() != null) {
mWindowManager.removeView(mView);
windowManager.removeView(mView);
}
try {
mWindowManager.addView(mView, mParams);
windowManager.addView(mView, mParams);
} catch (WindowManager.BadTokenException e) {
// Since the notification manager service cancels the token right after it notifies us
// to cancel the toast there is an inherent race and we may attempt to add a window