RemoteListenerHelper.java revision 4b3e3931270f8e406fc806bc7fa1c2788256687d
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.location;
18
19import com.android.internal.util.Preconditions;
20
21import android.annotation.NonNull;
22import android.os.IBinder;
23import android.os.IInterface;
24import android.os.RemoteException;
25import android.util.Log;
26
27import java.util.ArrayList;
28import java.util.Collection;
29import java.util.HashMap;
30
31/**
32 * A helper class, that handles operations in remote listeners, and tracks for remote process death.
33 */
34abstract class RemoteListenerHelper<TListener extends IInterface> {
35    private final String mTag;
36    private final HashMap<IBinder, LinkedListener> mListenerMap =
37            new HashMap<IBinder, LinkedListener>();
38
39    protected RemoteListenerHelper(String name) {
40        Preconditions.checkNotNull(name);
41        mTag = name;
42    }
43
44    public boolean addListener(@NonNull TListener listener) {
45        Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
46        if (!isSupported()) {
47            Log.e(mTag, "Refused to add listener, the feature is not supported.");
48            return false;
49        }
50
51        IBinder binder = listener.asBinder();
52        LinkedListener deathListener = new LinkedListener(listener);
53        synchronized (mListenerMap) {
54            if (mListenerMap.containsKey(binder)) {
55                // listener already added
56                return true;
57            }
58
59            try {
60                binder.linkToDeath(deathListener, 0 /* flags */);
61            } catch (RemoteException e) {
62                // if the remote process registering the listener is already death, just swallow the
63                // exception and continue
64                Log.e(mTag, "Remote listener already died.", e);
65                return false;
66            }
67
68            mListenerMap.put(binder, deathListener);
69            if (mListenerMap.size() == 1) {
70                if (!registerWithService()) {
71                    Log.e(mTag, "RegisterWithService failed, listener will be removed.");
72                    removeListener(listener);
73                    return false;
74                }
75            }
76        }
77
78        return true;
79    }
80
81    public boolean removeListener(@NonNull TListener listener) {
82        Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
83        if (!isSupported()) {
84            Log.e(mTag, "Refused to remove listener, the feature is not supported.");
85            return false;
86        }
87
88        IBinder binder = listener.asBinder();
89        LinkedListener linkedListener;
90        synchronized (mListenerMap) {
91            linkedListener = mListenerMap.remove(binder);
92            if (mListenerMap.isEmpty() && linkedListener != null) {
93                unregisterFromService();
94            }
95        }
96
97        if (linkedListener != null) {
98            binder.unlinkToDeath(linkedListener, 0 /* flags */);
99        }
100        return true;
101    }
102
103    protected abstract boolean isSupported();
104    protected abstract boolean registerWithService();
105    protected abstract void unregisterFromService();
106
107    protected interface ListenerOperation<TListener extends IInterface> {
108        void execute(TListener listener) throws RemoteException;
109    }
110
111    protected void foreach(ListenerOperation operation) {
112        Collection<LinkedListener> linkedListeners;
113        synchronized (mListenerMap) {
114            Collection<LinkedListener> values = mListenerMap.values();
115            linkedListeners = new ArrayList<LinkedListener>(values);
116        }
117
118        for (LinkedListener linkedListener : linkedListeners) {
119            TListener listener = linkedListener.getUnderlyingListener();
120            try {
121                operation.execute(listener);
122            } catch (RemoteException e) {
123                Log.e(mTag, "Error in monitored listener.", e);
124                removeListener(listener);
125            }
126        }
127    }
128
129    private class LinkedListener implements IBinder.DeathRecipient {
130        private final TListener mListener;
131
132        public LinkedListener(@NonNull TListener listener) {
133            mListener = listener;
134        }
135
136        @NonNull
137        public TListener getUnderlyingListener() {
138            return mListener;
139        }
140
141        @Override
142        public void binderDied() {
143            Log.d(mTag, "Remote Listener died: " + mListener);
144            removeListener(mListener);
145        }
146    }
147}
148