aboutsummaryrefslogtreecommitdiff
path: root/ResolverEventReporter.cpp
blob: a809c88c9a8e4bd2fc3840876ec0ebe90b02625d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define LOG_TAG "resolv"

#include "ResolverEventReporter.h"

#include <android-base/logging.h>
#include <android/binder_manager.h>

using aidl::android::net::metrics::INetdEventListener;
using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener;

ResolverEventReporter& ResolverEventReporter::getInstance() {
    // It should be initialized only once.
    static ResolverEventReporter instance;

    // Add framework metrics listener. Because the binder service "netd_listener" may be launched
    // later than Netd, try to get binder handler in every instance query if any. The framework
    // metrics listener should be added only once if it has been already added successfully.
    instance.addDefaultListener();

    return instance;
}

ResolverEventReporter::ListenerSet ResolverEventReporter::getListeners() const {
    return getListenersImpl();
}

ResolverEventReporter::UnsolEventListenerSet ResolverEventReporter::getUnsolEventListeners() const {
    return getUnsolEventListenersImpl();
}

int ResolverEventReporter::addListener(const std::shared_ptr<INetdEventListener>& listener) {
    return addListenerImpl(listener);
}

int ResolverEventReporter::addUnsolEventListener(
        const std::shared_ptr<IDnsResolverUnsolicitedEventListener>& listener) {
    return addUnsolEventListenerImpl(listener);
}

// TODO: Consider registering metrics listener from framework and remove this function.
// Currently, the framework listener "netd_listener" is shared by netd and libnetd_resolv.
// Consider breaking it into two listeners. Once it has done, may let framework register
// the listener proactively.
void ResolverEventReporter::addDefaultListener() {
    std::lock_guard lock(mMutex);

    static bool added = false;
    if (added) return;

    // Use the non-blocking call AServiceManager_checkService in order not to delay DNS
    // lookup threads when the netd_listener service is not ready.
    ndk::SpAIBinder binder = ndk::SpAIBinder(AServiceManager_checkService("netd_listener"));
    std::shared_ptr<INetdEventListener> listener = INetdEventListener::fromBinder(binder);

    if (listener == nullptr) return;

    if (!addListenerImplLocked(listener)) added = true;
}

void ResolverEventReporter::handleBinderDied(const void* who) {
    std::lock_guard lock(mMutex);

    // Use the raw binder pointer address to be the identification of dead binder. Treat "who"
    // which passes the raw address of dead binder as an identification only.
    auto found = std::find_if(mListeners.begin(), mListeners.end(),
                              [=](const auto& it) { return static_cast<void*>(it.get()) == who; });

    if (found != mListeners.end()) mListeners.erase(found);
}

void ResolverEventReporter::handleUnsolEventBinderDied(const void* who) {
    std::lock_guard lock(mMutex);

    // Use the raw binder pointer address to be the identification of dead binder. Treat "who"
    // which passes the raw address of dead binder as an identification only.
    auto found = std::find_if(mUnsolEventListeners.begin(), mUnsolEventListeners.end(),
                              [=](const auto& it) { return static_cast<void*>(it.get()) == who; });

    if (found != mUnsolEventListeners.end()) mUnsolEventListeners.erase(found);
}

ResolverEventReporter::ListenerSet ResolverEventReporter::getListenersImpl() const {
    std::lock_guard lock(mMutex);
    return mListeners;
}

ResolverEventReporter::UnsolEventListenerSet ResolverEventReporter::getUnsolEventListenersImpl()
        const {
    std::lock_guard lock(mMutex);
    return mUnsolEventListeners;
}

int ResolverEventReporter::addListenerImpl(const std::shared_ptr<INetdEventListener>& listener) {
    std::lock_guard lock(mMutex);
    return addListenerImplLocked(listener);
}

int ResolverEventReporter::addListenerImplLocked(
        const std::shared_ptr<INetdEventListener>& listener) {
    if (listener == nullptr) {
        LOG(ERROR) << "The listener should not be null";
        return -EINVAL;
    }

    for (const auto& it : mListeners) {
        if (it->asBinder().get() == listener->asBinder().get()) {
            LOG(WARNING) << "The listener was already subscribed";
            return -EEXIST;
        }
    }

    static AIBinder_DeathRecipient* deathRecipient = nullptr;
    if (deathRecipient == nullptr) {
        // The AIBinder_DeathRecipient object is used to manage all death recipients for multiple
        // binder objects. It doesn't released because there should have at least one binder object
        // from framework.
        // TODO: Considering to remove death recipient for the binder object from framework because
        // it doesn't need death recipient actually.
        deathRecipient = AIBinder_DeathRecipient_new([](void* cookie) {
            ResolverEventReporter::getInstance().handleBinderDied(cookie);
        });
    }

    // Pass the raw binder pointer address to be the cookie of the death recipient. While the death
    // notification is fired, the cookie is used for identifying which binder was died. Because
    // the NDK binder doesn't pass dead binder pointer to binder death handler, the binder death
    // handler can't know who was died via wp<IBinder>. The reason for wp<IBinder> is not passed
    // is that NDK binder can't transform a wp<IBinder> to a wp<AIBinder> in some cases.
    // See more information in b/128712772.
    auto binder = listener->asBinder().get();
    auto cookie = static_cast<void*>(listener.get());  // Used for dead binder identification.
    binder_status_t status = AIBinder_linkToDeath(binder, deathRecipient, cookie);

    if (STATUS_OK != status) {
        LOG(ERROR) << "Failed to register death notification for INetdEventListener";
        return -EAGAIN;
    }

    mListeners.insert(listener);
    return 0;
}

int ResolverEventReporter::addUnsolEventListenerImpl(
        const std::shared_ptr<IDnsResolverUnsolicitedEventListener>& listener) {
    std::lock_guard lock(mMutex);
    return addUnsolEventListenerImplLocked(listener);
}

int ResolverEventReporter::addUnsolEventListenerImplLocked(
        const std::shared_ptr<IDnsResolverUnsolicitedEventListener>& listener) {
    if (listener == nullptr) {
        LOG(ERROR) << "The unsolicited event listener should not be null";
        return -EINVAL;
    }

    for (const auto& it : mUnsolEventListeners) {
        if (it->asBinder().get() == listener->asBinder().get()) {
            LOG(WARNING) << "The unsolicited event listener was already subscribed";
            return -EEXIST;
        }
    }

    static AIBinder_DeathRecipient* deathRecipient = nullptr;
    if (deathRecipient == nullptr) {
        // The AIBinder_DeathRecipient object is used to manage all death recipients for multiple
        // binder objects. It doesn't released because there should have at least one binder object
        // from framework.
        // TODO: Considering to remove death recipient for the binder object from framework because
        // it doesn't need death recipient actually.
        deathRecipient = AIBinder_DeathRecipient_new([](void* cookie) {
            ResolverEventReporter::getInstance().handleUnsolEventBinderDied(cookie);
        });
    }

    // Pass the raw binder pointer address to be the cookie of the death recipient. While the death
    // notification is fired, the cookie is used for identifying which binder was died. Because
    // the NDK binder doesn't pass dead binder pointer to binder death handler, the binder death
    // handler can't know who was died via wp<IBinder>. The reason for wp<IBinder> is not passed
    // is that NDK binder can't transform a wp<IBinder> to a wp<AIBinder> in some cases.
    // See more information in b/128712772.
    auto binder = listener->asBinder().get();
    auto cookie = static_cast<void*>(listener.get());  // Used for dead binder identification.
    binder_status_t status = AIBinder_linkToDeath(binder, deathRecipient, cookie);

    if (STATUS_OK != status) {
        LOG(ERROR)
                << "Failed to register death notification for IDnsResolverUnsolicitedEventListener";
        return -EAGAIN;
    }

    mUnsolEventListeners.insert(listener);
    return 0;
}