blob: 79335f73060c12468586d8c13517ed0942dcdb5b [file] [log] [blame]
destradaaea8a8a62014-06-23 18:19:03 -07001/*
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}