1/*
2 * Copyright (C) 2013 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
17#define LOG_TAG "MediaLog"
18//#define LOG_NDEBUG 0
19
20#include <sys/mman.h>
21#include <utils/Log.h>
22#include <binder/PermissionCache.h>
23#include <media/nblog/NBLog.h>
24#include <private/android_filesystem_config.h>
25#include "MediaLogService.h"
26
27namespace android {
28
29static const char kDeadlockedString[] = "MediaLogService may be deadlocked\n";
30
31// mMerger, mMergeReader, and mMergeThread all point to the same location in memory
32// mMergerShared. This is the local memory FIFO containing data merged from all
33// individual thread FIFOs in shared memory. mMergeThread is used to periodically
34// call NBLog::Merger::merge() to collect the data and write it to the FIFO, and call
35// NBLog::MergeReader::getAndProcessSnapshot to process the merged data.
36MediaLogService::MediaLogService() :
37    BnMediaLogService(),
38    mMergerShared((NBLog::Shared*) malloc(NBLog::Timeline::sharedSize(kMergeBufferSize))),
39    mMerger(mMergerShared, kMergeBufferSize),
40    mMergeReader(mMergerShared, kMergeBufferSize, mMerger),
41    mMergeThread(new NBLog::MergeThread(mMerger, mMergeReader))
42{
43    mMergeThread->run("MergeThread");
44}
45
46MediaLogService::~MediaLogService()
47{
48    mMergeThread->requestExit();
49    mMergeThread->setTimeoutUs(0);
50    mMergeThread->join();
51    free(mMergerShared);
52}
53
54void MediaLogService::registerWriter(const sp<IMemory>& shared, size_t size, const char *name)
55{
56    if (IPCThreadState::self()->getCallingUid() != AID_AUDIOSERVER || shared == 0 ||
57            size < kMinSize || size > kMaxSize || name == NULL ||
58            shared->size() < NBLog::Timeline::sharedSize(size)) {
59        return;
60    }
61    sp<NBLog::Reader> reader(new NBLog::Reader(shared, size));
62    NBLog::NamedReader namedReader(reader, name);
63    Mutex::Autolock _l(mLock);
64    mNamedReaders.add(namedReader);
65    mMerger.addReader(namedReader);
66}
67
68void MediaLogService::unregisterWriter(const sp<IMemory>& shared)
69{
70    if (IPCThreadState::self()->getCallingUid() != AID_AUDIOSERVER || shared == 0) {
71        return;
72    }
73    Mutex::Autolock _l(mLock);
74    for (size_t i = 0; i < mNamedReaders.size(); ) {
75        if (mNamedReaders[i].reader()->isIMemory(shared)) {
76            mNamedReaders.removeAt(i);
77        } else {
78            i++;
79        }
80    }
81}
82
83bool MediaLogService::dumpTryLock(Mutex& mutex)
84{
85    bool locked = false;
86    for (int i = 0; i < kDumpLockRetries; ++i) {
87        if (mutex.tryLock() == NO_ERROR) {
88            locked = true;
89            break;
90        }
91        usleep(kDumpLockSleepUs);
92    }
93    return locked;
94}
95
96status_t MediaLogService::dump(int fd, const Vector<String16>& args __unused)
97{
98    // FIXME merge with similar but not identical code at services/audioflinger/ServiceUtilities.cpp
99    static const String16 sDump("android.permission.DUMP");
100    if (!(IPCThreadState::self()->getCallingUid() == AID_AUDIOSERVER ||
101            PermissionCache::checkCallingPermission(sDump))) {
102        dprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n",
103                IPCThreadState::self()->getCallingPid(),
104                IPCThreadState::self()->getCallingUid());
105        return NO_ERROR;
106    }
107
108    if (args.size() > 0) {
109        const String8 arg0(args[0]);
110        if (!strcmp(arg0.string(), "-r")) {
111            // needed because mNamedReaders is protected by mLock
112            bool locked = dumpTryLock(mLock);
113
114            // failed to lock - MediaLogService is probably deadlocked
115            if (!locked) {
116                String8 result(kDeadlockedString);
117                if (fd >= 0) {
118                    write(fd, result.string(), result.size());
119                } else {
120                    ALOGW("%s:", result.string());
121                }
122                // TODO should we instead proceed to mMergeReader.dump? does it need lock?
123                return NO_ERROR;
124            }
125
126            for (const auto& namedReader : mNamedReaders) {
127                if (fd >= 0) {
128                    dprintf(fd, "\n%s:\n", namedReader.name());
129                } else {
130                    ALOGI("%s:", namedReader.name());
131                }
132            }
133            mLock.unlock();
134        }
135    }
136    mMergeReader.dump(fd);
137    return NO_ERROR;
138}
139
140status_t MediaLogService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
141        uint32_t flags)
142{
143    return BnMediaLogService::onTransact(code, data, reply, flags);
144}
145
146void MediaLogService::requestMergeWakeup() {
147    mMergeThread->wakeup();
148}
149
150}   // namespace android
151