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

Commit be70785f authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Make activity manager more robust in the face of app activity leaks.

This came up from bug #5601885: Memory increase (leak?) in system_server
Stingray MR1

This isn't *really* a leak in the system process -- it is a leak in an
application process that is causing the system process to keep around
a bunch of ActivityRecord objects longer than it should, until that app
process is ultimately killed.

Unfortunately these days leaking an ActivityRecord also often means
leaking a thumbnail, which is a big slab of memory.

So make the activity manager better about this, using a weak reference
from the handle the object has so we can still clean away most of the
state associated with the ActivityRecord even if the client side leaks
its own reference.

Change-Id: Idbab45e09749cdfb54899203da7981e7b3576e25
parent 24597eb7
Loading
Loading
Loading
Loading
+19 −18
Original line number Diff line number Diff line
@@ -1669,7 +1669,7 @@ public final class ActivityManagerService extends ActivityManagerNative
    final void setFocusedActivityLocked(ActivityRecord r) {
        if (mFocusedActivity != r) {
            mFocusedActivity = r;
            mWindowManager.setFocusedApp(r, true);
            mWindowManager.setFocusedApp(r.appToken, true);
        }
    }
@@ -2346,7 +2346,8 @@ public final class ActivityManagerService extends ActivityManagerNative
            // XXX we are not dealing with propagating grantedUriPermissions...
            // those are not yet exposed to user code, so there is no need.
            int res = mMainStack.startActivityLocked(r.app.thread, intent,
                    r.resolvedType, null, 0, aInfo, resultTo, resultWho,
                    r.resolvedType, null, 0, aInfo,
                    resultTo != null ? resultTo.appToken : null, resultWho,
                    requestCode, -1, r.launchedFromUid, false, false, null);
            Binder.restoreCallingIdentity(origId);
@@ -2429,10 +2430,10 @@ public final class ActivityManagerService extends ActivityManagerNative
                return;
            }
            final long origId = Binder.clearCallingIdentity();
            mWindowManager.setAppOrientation(r, requestedOrientation);
            mWindowManager.setAppOrientation(r.appToken, requestedOrientation);
            Configuration config = mWindowManager.updateOrientationFromAppTokens(
                    mConfiguration,
                    r.mayFreezeScreenLocked(r.app) ? r : null);
                    r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
            if (config != null) {
                r.frozenBeforeDestroy = true;
                if (!updateConfigurationLocked(config, r, false)) {
@@ -2449,7 +2450,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            if (r == null) {
                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
            }
            return mWindowManager.getAppOrientation(r);
            return mWindowManager.getAppOrientation(r.appToken);
        }
    }
@@ -2515,7 +2516,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            for (int i=0; i<activities.size(); i++) {
                ActivityRecord r = activities.get(i);
                if (!r.finishing) {
                    int index = mMainStack.indexOfTokenLocked(r);
                    int index = mMainStack.indexOfTokenLocked(r.appToken);
                    if (index >= 0) {
                        mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED,
                                null, "finish-heavy");
@@ -2617,7 +2618,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            int i;
            for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                if (r == token) {
                if (r.appToken == token) {
                    return true;
                }
                if (r.fullscreen && !r.finishing) {
@@ -2705,9 +2706,9 @@ public final class ActivityManagerService extends ActivityManagerNative
                    r.makeFinishing();
                    mMainStack.mHistory.remove(i);
                    r.takeFromHistory();
                    mWindowManager.removeAppToken(r);
                    mWindowManager.removeAppToken(r.appToken);
                    if (VALIDATE_TOKENS) {
                        mWindowManager.validateAppTokens(mMainStack.mHistory);
                        mMainStack.validateAppTokensLocked();
                    }
                    r.removeUriPermissionsLocked();
@@ -5173,10 +5174,10 @@ public final class ActivityManagerService extends ActivityManagerNative
        if (topThumbnail != null) {
            if (localLOGV) Slog.v(TAG, "Requesting top thumbnail");
            try {
                topThumbnail.requestThumbnail(topRecord);
                topThumbnail.requestThumbnail(topRecord.appToken);
            } catch (Exception e) {
                Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
                sendPendingThumbnail(null, topRecord, null, null, true);
                sendPendingThumbnail(null, topRecord.appToken, null, null, true);
            }
        }
@@ -5547,7 +5548,7 @@ public final class ActivityManagerService extends ActivityManagerNative
        TaskRecord lastTask = null;
        for (int i=0; i<N; i++) {
            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
            if (r == token) {
            if (r.appToken == token) {
                if (!onlyRoot || lastTask != r.task) {
                    return r.task.taskId;
                }
@@ -5568,7 +5569,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            for (int i=0; i<N; i++) {
                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                if (r.realActivity.equals(className)
                        && r != token && lastTask != r.task) {
                        && r.appToken != token && lastTask != r.task) {
                    if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
                            null, "others")) {
                        i--;
@@ -7112,7 +7113,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                // process, then terminate it to avoid getting in a loop.
                Slog.w(TAG, "  Force finishing activity "
                        + r.intent.getComponent().flattenToShortString());
                int index = mMainStack.indexOfTokenLocked(r);
                int index = mMainStack.indexOfActivityLocked(r);
                r.stack.finishActivityLocked(r, index,
                        Activity.RESULT_CANCELED, null, "crashed");
                // Also terminate any activities below it that aren't yet
@@ -8631,8 +8632,8 @@ public final class ActivityManagerService extends ActivityManagerNative
            try {
                TransferPipe tp = new TransferPipe();
                try {
                    r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), r,
                            innerPrefix, args);
                    r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
                            r.appToken, innerPrefix, args);
                    tp.go(fd);
                } finally {
                    tp.kill();
@@ -9048,8 +9049,8 @@ public final class ActivityManagerService extends ActivityManagerNative
                try {
                    TransferPipe tp = new TransferPipe();
                    try {
                        r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), r,
                                innerPrefix, args);
                        r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
                                r.appToken, innerPrefix, args);
                        // Short timeout, since blocking here can
                        // deadlock with the application.
                        tp.go(fd, 2000);
+74 −7
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
@@ -48,9 +49,10 @@ import java.util.HashSet;
/**
 * An entry in the history stack, representing an activity.
 */
final class ActivityRecord extends IApplicationToken.Stub {
final class ActivityRecord {
    final ActivityManagerService service; // owner
    final ActivityStack stack; // owner
    final IApplicationToken.Stub appToken; // window manager token
    final ActivityInfo info; // all about me
    final int launchedFromUid; // always the uid who started the activity.
    final Intent intent;    // the original intent that generated us
@@ -200,6 +202,70 @@ final class ActivityRecord extends IApplicationToken.Stub {
        }
    }

    static class Token extends IApplicationToken.Stub {
        final WeakReference<ActivityRecord> weakActivity;

        Token(ActivityRecord activity) {
            weakActivity = new WeakReference<ActivityRecord>(activity);
        }

        @Override public void windowsDrawn() throws RemoteException {
            ActivityRecord activity = weakActivity.get();
            if (activity != null) {
                activity.windowsDrawn();
            }
        }

        @Override public void windowsVisible() throws RemoteException {
            ActivityRecord activity = weakActivity.get();
            if (activity != null) {
                activity.windowsVisible();
            }
        }

        @Override public void windowsGone() throws RemoteException {
            ActivityRecord activity = weakActivity.get();
            if (activity != null) {
                activity.windowsGone();
            }
        }

        @Override public boolean keyDispatchingTimedOut() throws RemoteException {
            ActivityRecord activity = weakActivity.get();
            if (activity != null) {
                return activity.keyDispatchingTimedOut();
            }
            return false;
        }

        @Override public long getKeyDispatchingTimeout() throws RemoteException {
            ActivityRecord activity = weakActivity.get();
            if (activity != null) {
                return activity.getKeyDispatchingTimeout();
            }
            return 0;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("Token{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(' ');
            sb.append(weakActivity.get());
            sb.append('}');
            return sb.toString();
        }
    }

    static ActivityRecord forToken(IBinder token) {
        try {
            return token != null ? ((Token)token).weakActivity.get() : null;
        } catch (ClassCastException e) {
            Slog.w(ActivityManagerService.TAG, "Bad activity token: " + token, e);
            return null;
        }
    }

    ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller,
            int _launchedFromUid, Intent _intent, String _resolvedType,
            ActivityInfo aInfo, Configuration _configuration,
@@ -207,6 +273,7 @@ final class ActivityRecord extends IApplicationToken.Stub {
            boolean _componentSpecified) {
        service = _service;
        stack = _stack;
        appToken = new Token(this);
        info = aInfo;
        launchedFromUid = _launchedFromUid;
        intent = _intent;
@@ -445,7 +512,7 @@ final class ActivityRecord extends IApplicationToken.Stub {
                ar.add(intent);
                service.grantUriPermissionFromIntentLocked(callingUid, packageName,
                        intent, getUriPermissionsLocked());
                app.thread.scheduleNewIntent(ar, this);
                app.thread.scheduleNewIntent(ar, appToken);
                sent = true;
            } catch (RemoteException e) {
                Slog.w(ActivityManagerService.TAG,
@@ -470,14 +537,14 @@ final class ActivityRecord extends IApplicationToken.Stub {
    void pauseKeyDispatchingLocked() {
        if (!keysPaused) {
            keysPaused = true;
            service.mWindowManager.pauseKeyDispatching(this);
            service.mWindowManager.pauseKeyDispatching(appToken);
        }
    }

    void resumeKeyDispatchingLocked() {
        if (keysPaused) {
            keysPaused = false;
            service.mWindowManager.resumeKeyDispatching(this);
            service.mWindowManager.resumeKeyDispatching(appToken);
        }
    }

@@ -512,14 +579,14 @@ final class ActivityRecord extends IApplicationToken.Stub {
    
    public void startFreezingScreenLocked(ProcessRecord app, int configChanges) {
        if (mayFreezeScreenLocked(app)) {
            service.mWindowManager.startAppFreezingScreen(this, configChanges);
            service.mWindowManager.startAppFreezingScreen(appToken, configChanges);
        }
    }
    
    public void stopFreezingScreenLocked(boolean force) {
        if (force || frozenBeforeDestroy) {
            frozenBeforeDestroy = false;
            service.mWindowManager.stopAppFreezingScreen(this, force);
            service.mWindowManager.stopAppFreezingScreen(appToken, force);
        }
    }
    
@@ -687,7 +754,7 @@ final class ActivityRecord extends IApplicationToken.Stub {
        }
        if (app != null && app.thread != null) {
            try {
                app.thread.scheduleSleeping(this, _sleeping);
                app.thread.scheduleSleeping(appToken, _sleeping);
                if (sleeping && !stack.mGoingToSleepActivities.contains(this)) {
                    stack.mGoingToSleepActivities.add(this);
                }
+104 −93

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -3070,7 +3070,7 @@ public class WindowManagerService extends IWindowManager.Stub
    // Application Window Tokens
    // -------------------------------------------------------------

    public void validateAppTokens(List tokens) {
    public void validateAppTokens(List<IBinder> tokens) {
        int v = tokens.size()-1;
        int m = mAppTokens.size()-1;
        while (v >= 0 && m >= 0) {