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

Commit f982e75a authored by Guliz Tuncay's avatar Guliz Tuncay
Browse files

Use RemoteCallbackList to implement TSMS#mListeners

Previously, TSMS extends IBinder.DeathRecipient to implement its own
death recipient class to keep track of disconnected service listeners.
Using RemoteCallbackList simplifies the implementation.

Fixes: 35102403
Test: Manually tested as follows.
      1. Build and flash an OS image.
      2. Complete the setup wizard (if any).
      3. Make sure AOSP Keyboard (com.android.inputmethod.latin)
       is installed.
      4. adb shell settings put secure selected_spell_checker com.android.inputmethod.latin/.spellcheck.AndroidSpellCheckerService
      5. Observe that there are no bindings to spell
       checker service (SCS) by running
       adb shell dumpsys textservices
      6. Observe that there is no connection to SCS by running
       adb shell dumpsys activity services com.android.inputmethod.latin
       There should be no ConnectionRecord for
       AndroidSpellCheckerService
      7. Run a test program that has TextView and tap on one of the
       TextViews and type some text.
      8. Repeat steps 5 and 6 to observe there are now bindings and
       connection to SCS.
      9. Kill the app manually. Repeat steps 5 and 6 to see bindings
       and connections are removed again.
      10. Repeat 7 and 8.
      11. Kill SCS
        adb shell ps -A | grep com.android.inputmethod.latin | awk '{print $2}' | xargs adb shell kill -KILL
      12. Repeat steps 5 and 6 to observe that the bindings and the
        connection to SCS are preserved.
Change-Id: I38942765ed6bec6713757b1d5f325e7a633c2ba7
parent b82b7046
Loading
Loading
Loading
Loading
+21 −45
Original line number Original line Diff line number Diff line
@@ -49,6 +49,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder;
import android.os.Process;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager;
@@ -797,12 +798,13 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
                    pw.println(
                    pw.println(
                            "        " + "mScLocale=" + req.mLocale + " mUid=" + req.mUserId);
                            "        " + "mScLocale=" + req.mLocale + " mUid=" + req.mUserId);
                }
                }
                final int N = grp.mListeners.size();
                final int N = grp.mListeners.getRegisteredCallbackCount();
                for (int i = 0; i < N; i++) {
                for (int i = 0; i < N; i++) {
                    final InternalDeathRecipient listener = grp.mListeners.get(i);
                    final ISpellCheckerSessionListener mScListener =
                            grp.mListeners.getRegisteredCallbackItem(i);
                    pw.println("      " + "Listener #" + i + ":");
                    pw.println("      " + "Listener #" + i + ":");
                    pw.println("        " + "mScListener=" + listener.mScListener);
                    pw.println("        " + "mScListener=" + mScListener);
                    pw.println("        " + "mGroup=" + listener.mGroup);
                    pw.println("        " + "mGroup=" + grp);
                }
                }
            }
            }
            pw.println("");
            pw.println("");
@@ -840,7 +842,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
    private final class SpellCheckerBindGroup {
    private final class SpellCheckerBindGroup {
        private final String TAG = SpellCheckerBindGroup.class.getSimpleName();
        private final String TAG = SpellCheckerBindGroup.class.getSimpleName();
        private final InternalServiceConnection mInternalConnection;
        private final InternalServiceConnection mInternalConnection;
        private final ArrayList<InternalDeathRecipient> mListeners = new ArrayList<>();
        private final InternalDeathRecipients mListeners;
        private boolean mUnbindCalled;
        private boolean mUnbindCalled;
        private ISpellCheckerService mSpellChecker;
        private ISpellCheckerService mSpellChecker;
        private boolean mConnected;
        private boolean mConnected;
@@ -849,6 +851,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {


        public SpellCheckerBindGroup(InternalServiceConnection connection) {
        public SpellCheckerBindGroup(InternalServiceConnection connection) {
            mInternalConnection = connection;
            mInternalConnection = connection;
            mListeners = new InternalDeathRecipients(this);
        }
        }


        public void onServiceConnected(ISpellCheckerService spellChecker) {
        public void onServiceConnected(ISpellCheckerService spellChecker) {
@@ -881,26 +884,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
                Slog.w(TAG, "remove listener: " + listener.hashCode());
                Slog.w(TAG, "remove listener: " + listener.hashCode());
            }
            }
            synchronized(mSpellCheckerMap) {
            synchronized(mSpellCheckerMap) {
                final int size = mListeners.size();
                mListeners.unregister(listener);
                final ArrayList<InternalDeathRecipient> removeList = new ArrayList<>();
                for (int i = 0; i < size; ++i) {
                    final InternalDeathRecipient tempRecipient = mListeners.get(i);
                    if(tempRecipient.hasSpellCheckerListener(listener)) {
                        if (DBG) {
                            Slog.w(TAG, "found existing listener.");
                        }
                        removeList.add(tempRecipient);
                    }
                }
                final int removeSize = removeList.size();
                for (int i = 0; i < removeSize; ++i) {
                    if (DBG) {
                        Slog.w(TAG, "Remove " + removeList.get(i));
                    }
                    final InternalDeathRecipient idr = removeList.get(i);
                    idr.mScListener.asBinder().unlinkToDeath(idr, 0);
                    mListeners.remove(idr);
                }
                cleanLocked();
                cleanLocked();
            }
            }
        }
        }
@@ -914,7 +898,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
                return;
                return;
            }
            }
            // If there are no more active listeners, clean up.  Only do this once.
            // If there are no more active listeners, clean up.  Only do this once.
            if (!mListeners.isEmpty()) {
            if (mListeners.getRegisteredCallbackCount() > 0) {
                return;
                return;
            }
            }
            if (!mPendingSessionRequests.isEmpty()) {
            if (!mPendingSessionRequests.isEmpty()) {
@@ -938,12 +922,10 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
        public void removeAll() {
        public void removeAll() {
            Slog.e(TAG, "Remove the spell checker bind unexpectedly.");
            Slog.e(TAG, "Remove the spell checker bind unexpectedly.");
            synchronized(mSpellCheckerMap) {
            synchronized(mSpellCheckerMap) {
                final int size = mListeners.size();
                final int size = mListeners.getRegisteredCallbackCount();
                for (int i = 0; i < size; ++i) {
                for (int i = 0; i < size; ++i) {
                    final InternalDeathRecipient idr = mListeners.get(i);
                    mListeners.unregister(mListeners.getRegisteredCallbackItem(i));
                    idr.mScListener.asBinder().unlinkToDeath(idr, 0);
                }
                }
                mListeners.clear();
                mPendingSessionRequests.clear();
                mPendingSessionRequests.clear();
                mOnGoingSessionRequests.clear();
                mOnGoingSessionRequests.clear();
                cleanLocked();
                cleanLocked();
@@ -984,12 +966,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
                    return;
                    return;
                }
                }
                if (mOnGoingSessionRequests.remove(request)) {
                if (mOnGoingSessionRequests.remove(request)) {
                    final InternalDeathRecipient recipient =
                            new InternalDeathRecipient(this, request.mScListener);
                    try {
                    try {
                        request.mTsListener.onServiceConnected(newSession);
                        request.mTsListener.onServiceConnected(newSession);
                        request.mScListener.asBinder().linkToDeath(recipient, 0);
                        mListeners.register(request.mScListener);
                        mListeners.add(recipient);
                    } catch (RemoteException e) {
                    } catch (RemoteException e) {
                        // Technically this can happen if the spell checker client app is already
                        // Technically this can happen if the spell checker client app is already
                        // dead.  We can just forget about this request; the request is already
                        // dead.  We can just forget about this request; the request is already
@@ -1045,26 +1024,23 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
        }
        }
    }
    }


    private static final class InternalDeathRecipient implements IBinder.DeathRecipient {
    private final class InternalDeathRecipients extends
        public final ISpellCheckerSessionListener mScListener;
            RemoteCallbackList<ISpellCheckerSessionListener> {
        private final SpellCheckerBindGroup mGroup;
        private final SpellCheckerBindGroup mGroup;


        public InternalDeathRecipient(SpellCheckerBindGroup group,
        public InternalDeathRecipients(SpellCheckerBindGroup group) {
                ISpellCheckerSessionListener scListener) {
            mScListener = scListener;
            mGroup = group;
            mGroup = group;
        }
        }


        public boolean hasSpellCheckerListener(ISpellCheckerSessionListener listener) {
            return listener.asBinder().equals(mScListener.asBinder());
        }

        @Override
        @Override
        public void binderDied() {
        public void onCallbackDied(ISpellCheckerSessionListener listener) {
            mGroup.removeListener(mScListener);
            synchronized(mSpellCheckerMap) {
                mGroup.removeListener(listener);
            }
            }
        }
        }


    }

    private static final class ISpellCheckerServiceCallbackBinder
    private static final class ISpellCheckerServiceCallbackBinder
            extends ISpellCheckerServiceCallback.Stub {
            extends ISpellCheckerServiceCallback.Stub {
        @NonNull
        @NonNull