RemoteListenerHelper.java revision ea8a8a6076f04360de2d25b3e5853cde8026cd5f
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 static final String TAG = "RemoteListenerHelper";
36
37    private final HashMap<IBinder, LinkedListener> mListenerMap =
38            new HashMap<IBinder, LinkedListener>();
39
40    public boolean addListener(@NonNull TListener listener) {
41        Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
42
43        if (!isSupported()) {
44            Log.e(TAG, "Refused to add listener, the feature is not supported.");
45            return false;
46        }
47
48        IBinder binder = listener.asBinder();
49        LinkedListener deathListener = new LinkedListener(listener);
50        synchronized (mListenerMap) {
51            if (mListenerMap.containsKey(binder)) {
52                // listener already added
53                return true;
54            }
55
56            try {
57                binder.linkToDeath(deathListener, 0 /* flags */);
58            } catch (RemoteException e) {
59                // if the remote process registering the listener is already death, just swallow the
60                // exception and continue
61                Log.e(TAG, "Remote listener already died.", e);
62                return false;
63            }
64
65            mListenerMap.put(binder, deathListener);
66            if (mListenerMap.size() == 1) {
67                onFirstListenerAdded();
68            }
69        }
70
71        return true;
72    }
73
74    public boolean removeListener(@NonNull TListener listener) {
75        Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
76
77        if (!isSupported()) {
78            Log.e(TAG, "Refused to remove listener, the feature is not supported.");
79            return false;
80        }
81
82        IBinder binder = listener.asBinder();
83        LinkedListener linkedListener;
84        synchronized (mListenerMap) {
85            linkedListener = mListenerMap.remove(binder);
86            if (mListenerMap.isEmpty() && linkedListener != null) {
87                onLastListenerRemoved();
88            }
89        }
90
91        if (linkedListener != null) {
92            binder.unlinkToDeath(linkedListener, 0 /* flags */);
93        }
94
95        return true;
96    }
97
98    protected abstract boolean isSupported();
99
100    protected void onFirstListenerAdded() {
101        // event triggered when the first listener has been added
102    }
103
104    protected void onLastListenerRemoved() {
105        // event triggered when the last listener has bee removed
106    }
107
108    protected interface ListenerOperation<TListener extends IInterface> {
109        void execute(TListener listener) throws RemoteException;
110    }
111
112    protected void foreach(ListenerOperation operation) {
113        Collection<LinkedListener> linkedListeners;
114        synchronized (mListenerMap) {
115            Collection<LinkedListener> values = mListenerMap.values();
116            linkedListeners = new ArrayList<LinkedListener>(values);
117        }
118
119        for (LinkedListener linkedListener : linkedListeners) {
120            TListener listener = linkedListener.getUnderlyingListener();
121            try {
122                operation.execute(listener);
123            } catch (RemoteException e) {
124                Log.e(TAG, "Error in monitored listener.", e);
125                removeListener(listener);
126            }
127        }
128    }
129
130    private class LinkedListener implements IBinder.DeathRecipient {
131        private final TListener mListener;
132
133        public LinkedListener(@NonNull TListener listener) {
134            mListener = listener;
135        }
136
137        @NonNull
138        public TListener getUnderlyingListener() {
139            return mListener;
140        }
141
142        @Override
143        public void binderDied() {
144            Log.d(TAG, "Remote Listener died: " + mListener);
145            removeListener(mListener);
146        }
147    }
148}
149