Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b886fab authored by Jim Miller's avatar Jim Miller
Browse files

Fix 3106227: use WeakReferences for receivers in DigitalClock class

This works around a bug in the framework where LockScreen wouldn't
get GC'd under certain circumstances which would lead to an OOM
crash.  It now uses WeakReferences for observers inside the
DigitalClock container class and unregisters them if the containing
DigitalClock goes away.

Also removed mLive variable which was unused and could potentially
leak the receivers.

Left mAttached for debugging so we can use it to determine if the
calls to onAttachToWindow() and onDetachFromWindow() are grossly
unbalanced which may be the root cause of the original problem.

Have cleanUp() explicitly clear unused references to make
tracing through hprof references easier.

Change-Id: I99a7e0c356001b05eab5aa729564553666febfea
parent 5a40b8ef
Loading
Loading
Loading
Loading
+63 −40
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Typeface;
import android.os.Handler;
@@ -33,6 +32,7 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.lang.ref.WeakReference;
import java.text.DateFormatSymbols;
import java.util.Calendar;

@@ -49,24 +49,39 @@ public class DigitalClock extends LinearLayout {
    private TextView mTimeDisplay;
    private AmPm mAmPm;
    private ContentObserver mFormatChangeObserver;
    private boolean mLive = true;
    private boolean mAttached;
    private int mAttached = 0; // for debugging - tells us whether attach/detach is unbalanced

    /* called by system on minute ticks */
    private final Handler mHandler = new Handler();
    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
    private BroadcastReceiver mIntentReceiver;

    private static class TimeChangedReceiver extends BroadcastReceiver {
        private WeakReference<DigitalClock> mClock;
        private Context mContext;

        public TimeChangedReceiver(DigitalClock clock) {
            mClock = new WeakReference<DigitalClock>(clock);
            mContext = clock.getContext();
        }

        @Override
        public void onReceive(Context context, Intent intent) {
                if (mLive && intent.getAction().equals(
                            Intent.ACTION_TIMEZONE_CHANGED)) {
                    mCalendar = Calendar.getInstance();
                }
            // Post a runnable to avoid blocking the broadcast.
                mHandler.post(new Runnable() {
            final boolean timezoneChanged =
                    intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED);
            final DigitalClock clock = mClock.get();
            if (clock != null) {
                clock.mHandler.post(new Runnable() {
                    public void run() {
                            updateTime();
                        if (timezoneChanged) {
                            clock.mCalendar = Calendar.getInstance();
                        }
                        clock.updateTime();
                    }
                });
            } else {
                mContext.unregisterReceiver(this);
            }
        }
    };

@@ -94,14 +109,23 @@ public class DigitalClock extends LinearLayout {
        }
    }

    private class FormatChangeObserver extends ContentObserver {
        public FormatChangeObserver() {
    private static class FormatChangeObserver extends ContentObserver {
        private WeakReference<DigitalClock> mClock;
        private Context mContext;
        public FormatChangeObserver(DigitalClock clock) {
            super(new Handler());
            mClock = new WeakReference<DigitalClock>(clock);
            mContext = clock.getContext();
        }
        @Override
        public void onChange(boolean selfChange) {
            setDateFormat();
            updateTime();
            DigitalClock digitalClock = mClock.get();
            if (digitalClock != null) {
                digitalClock.setDateFormat();
                digitalClock.updateTime();
            } else {
                mContext.getContentResolver().unregisterContentObserver(this);
            }
        }
    }

@@ -129,11 +153,11 @@ public class DigitalClock extends LinearLayout {
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        if (mAttached) return;
        mAttached = true;
        mAttached++;

        if (mLive) {
        /* monitor time ticks, time changed, timezone */
        if (mIntentReceiver == null) {
            mIntentReceiver = new TimeChangedReceiver(this);
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_TIME_TICK);
            filter.addAction(Intent.ACTION_TIME_CHANGED);
@@ -142,9 +166,11 @@ public class DigitalClock extends LinearLayout {
        }

        /* monitor 12/24-hour display preference */
        mFormatChangeObserver = new FormatChangeObserver();
        if (mFormatChangeObserver == null) {
            mFormatChangeObserver = new FormatChangeObserver(this);
            mContext.getContentResolver().registerContentObserver(
                    Settings.System.CONTENT_URI, true, mFormatChangeObserver);
        }

        updateTime();
    }
@@ -153,16 +179,19 @@ public class DigitalClock extends LinearLayout {
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        if (!mAttached) return;
        mAttached = false;
        mAttached--;

        if (mLive) {
        if (mIntentReceiver != null) {
            mContext.unregisterReceiver(mIntentReceiver);
        }
        if (mFormatChangeObserver != null) {
            mContext.getContentResolver().unregisterContentObserver(
                    mFormatChangeObserver);
        }

        mFormatChangeObserver = null;
        mIntentReceiver = null;
    }

    void updateTime(Calendar c) {
        mCalendar = c;
@@ -170,9 +199,7 @@ public class DigitalClock extends LinearLayout {
    }

    private void updateTime() {
        if (mLive) {
        mCalendar.setTimeInMillis(System.currentTimeMillis());
        }

        CharSequence newTime = DateFormat.format(mFormat, mCalendar);
        mTimeDisplay.setText(newTime);
@@ -184,8 +211,4 @@ public class DigitalClock extends LinearLayout {
            ? M24 : M12;
        mAmPm.setShowAmPm(mFormat.equals(M12));
    }

    void setLive(boolean live) {
        mLive = live;
    }
}
+6 −3
Original line number Diff line number Diff line
@@ -62,8 +62,8 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree
     */
    private static final int AWAKE_POKE_MILLIS = 30000;

    private final KeyguardScreenCallback mCallback;
    private final LockPatternUtils mLockPatternUtils;
    private KeyguardScreenCallback mCallback;
    private LockPatternUtils mLockPatternUtils;
    private KeyguardUpdateMonitor mUpdateMonitor;

    private TextView mTopHeader;
@@ -159,7 +159,10 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree
        if (mCheckingDialog != null) {
            mCheckingDialog.hide();
        }
        mUpdateMonitor.removeCallback(this);
        mUpdateMonitor.removeCallback(this); // this must be first
        mCallback = null;
        mLockPatternUtils = null;
        mUpdateMonitor = null;
    }

    /** {@inheritDoc} */
+1 −1
Original line number Diff line number Diff line
@@ -223,8 +223,8 @@ public class KeyguardViewManager implements KeyguardWindowController {
                mKeyguardHost.postDelayed(new Runnable() {
                    public void run() {
                        synchronized (KeyguardViewManager.this) {
                            mKeyguardHost.removeView(lastView);
                            lastView.cleanUp();
                            mKeyguardHost.removeView(lastView);
                        }
                    }
                }, 500);
+2 −0
Original line number Diff line number Diff line
@@ -495,8 +495,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
    public void cleanUp() {
        ((KeyguardScreen) mLockScreen).onPause();
        ((KeyguardScreen) mLockScreen).cleanUp();
        this.removeView(mLockScreen);
        ((KeyguardScreen) mUnlockScreen).onPause();
        ((KeyguardScreen) mUnlockScreen).cleanUp();
        this.removeView(mUnlockScreen);
    }

    private boolean isSecure() {
+9 −6
Original line number Diff line number Diff line
@@ -55,9 +55,9 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM

    private Status mStatus = Status.Normal;

    private final LockPatternUtils mLockPatternUtils;
    private final KeyguardUpdateMonitor mUpdateMonitor;
    private final KeyguardScreenCallback mCallback;
    private LockPatternUtils mLockPatternUtils;
    private KeyguardUpdateMonitor mUpdateMonitor;
    private KeyguardScreenCallback mCallback;

    private TextView mCarrier;
    private SlidingTab mSelector;
@@ -225,8 +225,8 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM
        setFocusableInTouchMode(true);
        setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);

        updateMonitor.registerInfoCallback(this);
        updateMonitor.registerSimStateCallback(this);
        mUpdateMonitor.registerInfoCallback(this);
        mUpdateMonitor.registerSimStateCallback(this);

        mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
        mSilentMode = isSilentMode();
@@ -668,7 +668,10 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM

    /** {@inheritDoc} */
    public void cleanUp() {
        mUpdateMonitor.removeCallback(this);
        mUpdateMonitor.removeCallback(this); // this must be first
        mLockPatternUtils = null;
        mUpdateMonitor = null;
        mCallback = null;
    }

    /** {@inheritDoc} */
Loading