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

Commit 026b8680 authored by Mehdi Alizadeh's avatar Mehdi Alizadeh Committed by android-build-merger
Browse files

Merge "Restores state of AppPredictionService after remote service crash" into...

Merge "Restores state of AppPredictionService after remote service crash" into qt-dev am: b143fe49
am: 0531fd64

Change-Id: I6833d54f0f76d632ea22d31e5993fcd8028db058
parents 0b601c56 0531fd64
Loading
Loading
Loading
Loading
+134 −3
Original line number Diff line number Diff line
@@ -28,13 +28,17 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.appprediction.AppPredictionService;
import android.util.ArrayMap;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractPerUserSystemService;

import java.util.ArrayList;

/**
 * Per-user instance of {@link AppPredictionManagerService}.
 */
@@ -48,6 +52,17 @@ public class AppPredictionPerUserService extends
    @GuardedBy("mLock")
    private RemoteAppPredictionService mRemoteService;

    /**
     * When {@code true}, remote service died but service state is kept so it's restored after
     * the system re-binds to it.
     */
    @GuardedBy("mLock")
    private boolean mZombie;

    @GuardedBy("mLock")
    private final ArrayMap<AppPredictionSessionId, AppPredictionSessionInfo> mSessionInfos =
            new ArrayMap<>();

    protected AppPredictionPerUserService(AppPredictionManagerService master,
            Object lock, int userId) {
        super(master, lock, userId);
@@ -92,6 +107,16 @@ public class AppPredictionPerUserService extends
        final RemoteAppPredictionService service = getRemoteServiceLocked();
        if (service != null) {
            service.onCreatePredictionSession(context, sessionId);

            mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context, () -> {
                synchronized (mLock) {
                    AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
                    if (sessionInfo != null) {
                        sessionInfo.removeAllCallbacksLocked();
                        mSessionInfos.remove(sessionId);
                    }
                }
            }));
        }
    }

@@ -140,6 +165,11 @@ public class AppPredictionPerUserService extends
        final RemoteAppPredictionService service = getRemoteServiceLocked();
        if (service != null) {
            service.registerPredictionUpdates(sessionId, callback);

            AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
            if (sessionInfo != null) {
                sessionInfo.addCallbackLocked(callback);
            }
        }
    }

@@ -152,6 +182,11 @@ public class AppPredictionPerUserService extends
        final RemoteAppPredictionService service = getRemoteServiceLocked();
        if (service != null) {
            service.unregisterPredictionUpdates(sessionId, callback);

            AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
            if (sessionInfo != null) {
                sessionInfo.removeCallbackLocked(callback);
            }
        }
    }

@@ -174,6 +209,12 @@ public class AppPredictionPerUserService extends
        final RemoteAppPredictionService service = getRemoteServiceLocked();
        if (service != null) {
            service.onDestroyPredictionSession(sessionId);

            AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
            if (sessionInfo != null) {
                sessionInfo.removeAllCallbacksLocked();
                mSessionInfos.remove(sessionId);
            }
        }
    }

@@ -182,17 +223,54 @@ public class AppPredictionPerUserService extends
        if (isDebug()) {
            Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
        }

        // Do nothing, we are just proxying to the prediction service
    }

    @Override
    public void onConnectedStateChanged(boolean connected) {
        if (isDebug()) {
            Slog.d(TAG, "onConnectedStateChanged(): connected=" + connected);
        }
        if (connected) {
            synchronized (mLock) {
                if (mZombie) {
                    // Sanity check - shouldn't happen
                    if (mRemoteService == null) {
                        Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
                        return;
                    }
                    mZombie = false;
                    resurrectSessionsLocked();
                }
            }
        }
    }

    @Override
    public void onServiceDied(RemoteAppPredictionService service) {
        if (isDebug()) {
            Slog.d(TAG, "onServiceDied():");
            Slog.w(TAG, "onServiceDied(): service=" + service);
        }
        synchronized (mLock) {
            mZombie = true;
        }
        // Do nothing, eventually the system will bind to the remote service again...
    }

        // Do nothing, we are just proxying to the prediction service
    /**
     * Called after the remote service connected, it's used to restore state from a 'zombie'
     * service (i.e., after it died).
     */
    private void resurrectSessionsLocked() {
        final int numSessions = mSessionInfos.size();
        if (isDebug()) {
            Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
                    + numSessions + " sessions.");
        }

        for (AppPredictionSessionInfo sessionInfo : mSessionInfos.values()) {
            sessionInfo.resurrectSessionLocked(this);
        }
    }

    @GuardedBy("mLock")
@@ -215,4 +293,57 @@ public class AppPredictionPerUserService extends

        return mRemoteService;
    }

    private static final class AppPredictionSessionInfo {
        private final AppPredictionSessionId mSessionId;
        private final AppPredictionContext mContext;
        private final ArrayList<IPredictionCallback> mCallbacks = new ArrayList<>();
        private final IBinder.DeathRecipient mBinderDeathHandler;

        AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext context,
                IBinder.DeathRecipient binderDeathHandler) {
            mSessionId = id;
            mContext = context;
            mBinderDeathHandler = binderDeathHandler;
        }

        void addCallbackLocked(IPredictionCallback callback) {
            if (mBinderDeathHandler != null) {
                try {
                    callback.asBinder().linkToDeath(mBinderDeathHandler, 0);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Failed to link to death: " + e);
                }
            }
            mCallbacks.add(callback);
        }

        void removeCallbackLocked(IPredictionCallback callback) {
            if (mBinderDeathHandler != null) {
                callback.asBinder().unlinkToDeath(mBinderDeathHandler, 0);
            }
            mCallbacks.remove(callback);
        }

        void removeAllCallbacksLocked() {
            if (mBinderDeathHandler != null) {
                for (IPredictionCallback callback : mCallbacks) {
                    callback.asBinder().unlinkToDeath(mBinderDeathHandler, 0);
                }
            }
            mCallbacks.clear();
        }

        void resurrectSessionLocked(AppPredictionPerUserService service) {
            if (service.isDebug()) {
                Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
                        + ") for session Id=" + mSessionId + " and "
                        + mCallbacks.size() + " callbacks.");
            }
            service.onCreatePredictionSessionLocked(mContext, mSessionId);
            for (IPredictionCallback callback : mCallbacks) {
                service.registerPredictionUpdatesLocked(mSessionId, callback);
            }
        }
    }
}
+15 −0
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@ public class RemoteAppPredictionService extends

    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;

    private final RemoteAppPredictionServiceCallbacks mCallback;

    public RemoteAppPredictionService(Context context, String serviceInterface,
            ComponentName componentName, int userId,
            RemoteAppPredictionServiceCallbacks callback, boolean bindInstantServiceAllowed,
@@ -50,6 +52,7 @@ public class RemoteAppPredictionService extends
                context.getMainThreadHandler(),
                bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
                verbose, /* initialCapacity= */ 1);
        mCallback = callback;
    }

    @Override
@@ -141,5 +144,17 @@ public class RemoteAppPredictionService extends
         * Notifies a the failure or timeout of a remote call.
         */
        void onFailureOrTimeout(boolean timedOut);

        /**
         * Notifies change in connected state of the remote service.
         */
        void onConnectedStateChanged(boolean connected);
    }

    @Override // from AbstractRemoteService
    protected void handleOnConnectedStateChanged(boolean connected) {
        if (mCallback != null) {
            mCallback.onConnectedStateChanged(connected);
        }
    }
}