/*
 * Copyright 2015, 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 "BatteryNotifier"
//#define LOG_NDEBUG 0

#include "include/mediautils/BatteryNotifier.h"

#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <private/android_filesystem_config.h>

namespace android {

void BatteryNotifier::DeathNotifier::binderDied(const wp<IBinder>& /*who*/) {
    BatteryNotifier::getInstance().onBatteryStatServiceDied();
}

BatteryNotifier::BatteryNotifier() {}

BatteryNotifier::~BatteryNotifier() {
    Mutex::Autolock _l(mLock);
    if (mDeathNotifier != nullptr) {
        IInterface::asBinder(mBatteryStatService)->unlinkToDeath(mDeathNotifier);
    }
}

void BatteryNotifier::noteStartVideo(uid_t uid) {
    Mutex::Autolock _l(mLock);
    sp<IBatteryStats> batteryService = getBatteryService_l();
    if (mVideoRefCounts[uid] == 0 && batteryService != nullptr) {
        batteryService->noteStartVideo(uid);
    }
    mVideoRefCounts[uid]++;
}

void BatteryNotifier::noteStopVideo(uid_t uid) {
    Mutex::Autolock _l(mLock);
    if (mVideoRefCounts.find(uid) == mVideoRefCounts.end()) {
        ALOGW("%s: video refcount is broken for uid(%d).", __FUNCTION__, (int)uid);
        return;
    }

    sp<IBatteryStats> batteryService = getBatteryService_l();

    mVideoRefCounts[uid]--;
    if (mVideoRefCounts[uid] == 0) {
        if (batteryService != nullptr) {
            batteryService->noteStopVideo(uid);
        }
        mVideoRefCounts.erase(uid);
    }
}

void BatteryNotifier::noteResetVideo() {
    Mutex::Autolock _l(mLock);
    sp<IBatteryStats> batteryService = getBatteryService_l();
    mVideoRefCounts.clear();
    if (batteryService != nullptr) {
        batteryService->noteResetVideo();
    }
}

void BatteryNotifier::noteStartAudio(uid_t uid) {
    Mutex::Autolock _l(mLock);
    sp<IBatteryStats> batteryService = getBatteryService_l();
    if (mAudioRefCounts[uid] == 0 && batteryService != nullptr) {
        batteryService->noteStartAudio(uid);
    }
    mAudioRefCounts[uid]++;
}

void BatteryNotifier::noteStopAudio(uid_t uid) {
    Mutex::Autolock _l(mLock);
    if (mAudioRefCounts.find(uid) == mAudioRefCounts.end()) {
        ALOGW("%s: audio refcount is broken for uid(%d).", __FUNCTION__, (int)uid);
        return;
    }

    sp<IBatteryStats> batteryService = getBatteryService_l();

    mAudioRefCounts[uid]--;
    if (mAudioRefCounts[uid] == 0) {
        if (batteryService != nullptr) {
            batteryService->noteStopAudio(uid);
        }
        mAudioRefCounts.erase(uid);
    }
}

void BatteryNotifier::noteResetAudio() {
    Mutex::Autolock _l(mLock);
    sp<IBatteryStats> batteryService = getBatteryService_l();
    mAudioRefCounts.clear();
    if (batteryService != nullptr) {
        batteryService->noteResetAudio();
    }
}

void BatteryNotifier::noteFlashlightOn(const String8& id, uid_t uid) {
    Mutex::Autolock _l(mLock);
    sp<IBatteryStats> batteryService = getBatteryService_l();

    std::pair<String8, uid_t> k = std::make_pair(id, uid);
    if (!mFlashlightState[k]) {
        mFlashlightState[k] = true;
        if (batteryService != nullptr) {
            batteryService->noteFlashlightOn(uid);
        }
    }
}

void BatteryNotifier::noteFlashlightOff(const String8& id, uid_t uid) {
    Mutex::Autolock _l(mLock);
    sp<IBatteryStats> batteryService = getBatteryService_l();

    std::pair<String8, uid_t> k = std::make_pair(id, uid);
    if (mFlashlightState[k]) {
        mFlashlightState[k] = false;
        if (batteryService != nullptr) {
            batteryService->noteFlashlightOff(uid);
        }
    }
}

void BatteryNotifier::noteResetFlashlight() {
    Mutex::Autolock _l(mLock);
    sp<IBatteryStats> batteryService = getBatteryService_l();
    mFlashlightState.clear();
    if (batteryService != nullptr) {
        batteryService->noteResetFlashlight();
    }
}

void BatteryNotifier::noteStartCamera(const String8& id, uid_t uid) {
    Mutex::Autolock _l(mLock);
    sp<IBatteryStats> batteryService = getBatteryService_l();
    std::pair<String8, uid_t> k = std::make_pair(id, uid);
    if (!mCameraState[k]) {
        mCameraState[k] = true;
        if (batteryService != nullptr) {
            batteryService->noteStartCamera(uid);
        }
    }
}

void BatteryNotifier::noteStopCamera(const String8& id, uid_t uid) {
    Mutex::Autolock _l(mLock);
    sp<IBatteryStats> batteryService = getBatteryService_l();
    std::pair<String8, uid_t> k = std::make_pair(id, uid);
    if (mCameraState[k]) {
        mCameraState[k] = false;
        if (batteryService != nullptr) {
            batteryService->noteStopCamera(uid);
        }
    }
}

void BatteryNotifier::noteResetCamera() {
    Mutex::Autolock _l(mLock);
    sp<IBatteryStats> batteryService = getBatteryService_l();
    mCameraState.clear();
    if (batteryService != nullptr) {
        batteryService->noteResetCamera();
    }
}

void BatteryNotifier::onBatteryStatServiceDied() {
    Mutex::Autolock _l(mLock);
    mBatteryStatService.clear();
    mDeathNotifier.clear();
    // Do not reset mVideoRefCounts and mAudioRefCounts here. The ref
    // counting is independent of the battery service availability.
    // We need this if battery service becomes available after media
    // started.

}

sp<IBatteryStats> BatteryNotifier::getBatteryService_l() {
    if (mBatteryStatService != nullptr) {
        return mBatteryStatService;
    }
    // Get battery service from service manager
    const sp<IServiceManager> sm(defaultServiceManager());
    if (sm != nullptr) {
        const String16 name("batterystats");
        mBatteryStatService = interface_cast<IBatteryStats>(sm->checkService(name));
        if (mBatteryStatService == nullptr) {
            // this may occur normally during the init sequence as mediaserver
            // and audioserver start before the batterystats service is available.
            ALOGW("batterystats service unavailable!");
            return nullptr;
        }

        mDeathNotifier = new DeathNotifier();
        IInterface::asBinder(mBatteryStatService)->linkToDeath(mDeathNotifier);

        // Notify start now if mediaserver or audioserver is already started.
        // 1) mediaserver and audioserver is started before batterystats service
        // 2) batterystats server may have crashed.
        std::map<uid_t, int>::iterator it = mVideoRefCounts.begin();
        for (; it != mVideoRefCounts.end(); ++it) {
            mBatteryStatService->noteStartVideo(it->first);
        }
        it = mAudioRefCounts.begin();
        for (; it != mAudioRefCounts.end(); ++it) {
            mBatteryStatService->noteStartAudio(it->first);
        }
        // TODO: Notify for camera and flashlight state as well?
    }
    return mBatteryStatService;
}

ANDROID_SINGLETON_STATIC_INSTANCE(BatteryNotifier);

}  // namespace android