Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9df2d39c authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk Committed by Android (Google) Code Review
Browse files

Merge "Implement handling hardware failures of ITuner."

parents e0bf11e1 f13b8419
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ public class RadioService extends SystemService {
     */
    private final long mNativeContext = nativeInit();

    private final Object mLock = new Object();

    public RadioService(Context context) {
        super(context);
    }
@@ -61,11 +63,13 @@ public class RadioService extends SystemService {
        @Override
        public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
                boolean withAudio, ITunerCallback callback) {
            // TODO(b/36863239): add death monitoring for binder
            if (callback == null) {
                throw new IllegalArgumentException("Callback must not be empty");
            }
            synchronized (mLock) {
                // TODO(b/36863239): add death monitoring for binder
                return nativeOpenTuner(mNativeContext, moduleId, bandConfig, withAudio, callback);
            }
        }
    }
}
+23 −2
Original line number Diff line number Diff line
@@ -31,15 +31,18 @@ class Tuner extends ITuner.Stub {
     */
    private final long mNativeContext;

    @NonNull private final TunerCallback mTunerCallback;
    private final Object mLock = new Object();
    private boolean mIsClosed = false;
    private boolean mIsMuted = false;
    private int mRegion;  // TODO(b/36863239): find better solution to manage regions
    private final boolean mWithAudio;

    Tuner(@NonNull ITunerCallback clientCallback, int halRev, int region, boolean withAudio) {
        mTunerCallback = new TunerCallback(this, clientCallback, halRev);
        mRegion = region;
        mWithAudio = withAudio;
        mNativeContext = nativeInit(clientCallback, halRev);
        mNativeContext = nativeInit(halRev);
    }

    @Override
@@ -48,7 +51,7 @@ class Tuner extends ITuner.Stub {
        super.finalize();
    }

    private native long nativeInit(@NonNull ITunerCallback clientCallback, int halRev);
    private native long nativeInit(int halRev);
    private native void nativeFinalize(long nativeContext);
    private native void nativeClose(long nativeContext);

@@ -66,7 +69,16 @@ class Tuner extends ITuner.Stub {
    @Override
    public void close() {
        synchronized (mLock) {
            if (mIsClosed) return;
            mTunerCallback.detach();
            nativeClose(mNativeContext);
            mIsClosed = true;
        }
    }

    private void checkNotClosedLocked() {
        if (mIsClosed) {
            throw new IllegalStateException("Tuner is closed, no further operations are allowed");
        }
    }

@@ -76,6 +88,7 @@ class Tuner extends ITuner.Stub {
            throw new IllegalArgumentException("The argument must not be a null pointer");
        }
        synchronized (mLock) {
            checkNotClosedLocked();
            nativeSetConfiguration(mNativeContext, config);
            mRegion = config.getRegion();
        }
@@ -84,6 +97,7 @@ class Tuner extends ITuner.Stub {
    @Override
    public RadioManager.BandConfig getConfiguration() {
        synchronized (mLock) {
            checkNotClosedLocked();
            return nativeGetConfiguration(mNativeContext, mRegion);
        }
    }
@@ -94,6 +108,7 @@ class Tuner extends ITuner.Stub {
            throw new IllegalStateException("Can't operate on mute - no audio requested");
        }
        synchronized (mLock) {
            checkNotClosedLocked();
            if (mIsMuted == mute) return;
            mIsMuted = mute;

@@ -109,6 +124,7 @@ class Tuner extends ITuner.Stub {
            return true;
        }
        synchronized (mLock) {
            checkNotClosedLocked();
            return mIsMuted;
        }
    }
@@ -116,6 +132,7 @@ class Tuner extends ITuner.Stub {
    @Override
    public void step(boolean directionDown, boolean skipSubChannel) {
        synchronized (mLock) {
            checkNotClosedLocked();
            nativeStep(mNativeContext, directionDown, skipSubChannel);
        }
    }
@@ -123,6 +140,7 @@ class Tuner extends ITuner.Stub {
    @Override
    public void scan(boolean directionDown, boolean skipSubChannel) {
        synchronized (mLock) {
            checkNotClosedLocked();
            nativeScan(mNativeContext, directionDown, skipSubChannel);
        }
    }
@@ -130,6 +148,7 @@ class Tuner extends ITuner.Stub {
    @Override
    public void tune(int channel, int subChannel) {
        synchronized (mLock) {
            checkNotClosedLocked();
            nativeTune(mNativeContext, channel, subChannel);
        }
    }
@@ -137,6 +156,7 @@ class Tuner extends ITuner.Stub {
    @Override
    public void cancel() {
        synchronized (mLock) {
            checkNotClosedLocked();
            nativeCancel(mNativeContext);
        }
    }
@@ -144,6 +164,7 @@ class Tuner extends ITuner.Stub {
    @Override
    public RadioManager.ProgramInfo getProgramInformation() {
        synchronized (mLock) {
            checkNotClosedLocked();
            return nativeGetProgramInformation(mNativeContext);
        }
    }
+97 −0
Original line number Diff line number Diff line
/**
 * Copyright (C) 2017 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.
 */

package com.android.server.radio;

import android.annotation.NonNull;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioTuner;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;

class TunerCallback implements ITunerCallback {
    // TODO(b/36863239): rename to RadioService.TunerCallback when native service goes away
    private static final String TAG = "RadioServiceJava.TunerCallback";

    /**
     * This field is used by native code, do not access or modify.
     */
    private final long mNativeContext;

    @NonNull private final Tuner mTuner;
    @NonNull private final ITunerCallback mClientCallback;

    TunerCallback(@NonNull Tuner tuner, @NonNull ITunerCallback clientCallback, int halRev) {
        mTuner = tuner;
        mClientCallback = clientCallback;
        mNativeContext = nativeInit(tuner, halRev);
    }

    @Override
    protected void finalize() throws Throwable {
        nativeFinalize(mNativeContext);
        super.finalize();
    }

    private native long nativeInit(@NonNull Tuner tuner, int halRev);
    private native void nativeFinalize(long nativeContext);
    private native void nativeDetach(long nativeContext);

    public void detach() {
        nativeDetach(mNativeContext);
    }

    // called from native side
    private void handleHwFailure() {
        onError(RadioTuner.ERROR_HARDWARE_FAILURE);
        mTuner.close();
    }

    @Override
    public void onError(int status) {
        try {
            mClientCallback.onError(status);
        } catch (RemoteException e) {
            Slog.e(TAG, "client died", e);
        }
    }

    @Override
    public void onConfigurationChanged(RadioManager.BandConfig config) {
        try {
            mClientCallback.onConfigurationChanged(config);
        } catch (RemoteException e) {
            Slog.e(TAG, "client died", e);
        }
    }

    @Override
    public void onProgramInfoChanged(RadioManager.ProgramInfo info) {
        try {
            mClientCallback.onProgramInfoChanged(info);
        } catch (RemoteException e) {
            Slog.e(TAG, "client died", e);
        }
    }

    @Override
    public IBinder asBinder() {
        throw new RuntimeException("Not a binder");
    }
}
+17 −9
Original line number Diff line number Diff line
@@ -49,10 +49,16 @@ using V1_0::ITuner;

static Mutex gContextMutex;

static jclass gTunerClass;
static jmethodID gTunerCstor;
static struct {
    struct {
        jclass clazz;
        jmethodID cstor;
    } Tuner;

static jclass gServiceClass;
    struct {
        jclass clazz;
    } RadioService;
} gjni;

struct ServiceContext {
    ServiceContext() {}
@@ -123,6 +129,8 @@ static sp<V1_0::IBroadcastRadio> getModule(jlong nativeContext) {
static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jint moduleId,
        jobject bandConfig, bool withAudio, jobject callback) {
    ALOGV("nativeOpenTuner()");
    EnvWrapper wrap(env);

    if (callback == nullptr) {
        ALOGE("Callback is empty");
        return nullptr;
@@ -146,7 +154,8 @@ static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jin
    Region region;
    BandConfig bandConfigHal = convert::BandConfigToHal(env, bandConfig, region);

    jobject tuner = env->NewObject(gTunerClass, gTunerCstor, callback, halRev, region, withAudio);
    auto tuner = wrap(env->NewObject(gjni.Tuner.clazz, gjni.Tuner.cstor,
            callback, halRev, region, withAudio));
    if (tuner == nullptr) {
        ALOGE("Unable to create new tuner object.");
        return nullptr;
@@ -165,13 +174,12 @@ static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jin
        ALOGE("Couldn't open tuner");
        ALOGE_IF(hidlResult.isOk(), "halResult = %d", halResult);
        ALOGE_IF(!hidlResult.isOk(), "hidlResult = %s", hidlResult.description().c_str());
        env->DeleteLocalRef(tuner);
        return nullptr;
    }

    Tuner::setHalTuner(env, tuner, halTuner);
    ALOGI("Opened tuner %p", halTuner.get());
    return tuner;
    return tuner.release();
}

static const JNINativeMethod gRadioServiceMethods[] = {
@@ -192,12 +200,12 @@ void register_android_server_radio_RadioService(JNIEnv *env) {
    register_android_server_radio_convert(env);

    auto tunerClass = FindClassOrDie(env, "com/android/server/radio/Tuner");
    gTunerClass = MakeGlobalRefOrDie(env, tunerClass);
    gTunerCstor = GetMethodIDOrDie(env, tunerClass, "<init>",
    gjni.Tuner.clazz = MakeGlobalRefOrDie(env, tunerClass);
    gjni.Tuner.cstor = GetMethodIDOrDie(env, tunerClass, "<init>",
            "(Landroid/hardware/radio/ITunerCallback;IIZ)V");

    auto serviceClass = FindClassOrDie(env, "com/android/server/radio/RadioService");
    gServiceClass = MakeGlobalRefOrDie(env, serviceClass);
    gjni.RadioService.clazz = MakeGlobalRefOrDie(env, serviceClass);

    auto res = jniRegisterNativeMethods(env, "com/android/server/radio/RadioService",
            gRadioServiceMethods, NELEM(gRadioServiceMethods));
+23 −19
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ static struct {
    struct {
        jfieldID nativeContext;
        jfieldID region;
        jfieldID tunerCallback;
    } Tuner;
} gjni;

@@ -59,7 +60,6 @@ struct TunerContext {
    HalRevision mHalRev;
    sp<V1_0::ITuner> mHalTuner;
    sp<V1_1::ITuner> mHalTuner11;
    sp<TunerCallback> mNativeCallback;

private:
    DISALLOW_COPY_AND_ASSIGN(TunerContext);
@@ -74,17 +74,16 @@ static TunerContext& getNativeContext(jlong nativeContextHandle) {
/**
 * Always lock gContextMutex when using native context.
 */
static TunerContext& getNativeContext(JNIEnv *env, jobject obj) {
    return getNativeContext(env->GetLongField(obj, gjni.Tuner.nativeContext));
static TunerContext& getNativeContext(JNIEnv *env, JavaRef<jobject> const &jTuner) {
    return getNativeContext(env->GetLongField(jTuner.get(), gjni.Tuner.nativeContext));
}

static jlong nativeInit(JNIEnv *env, jobject obj, jobject clientCallback, jint halRev) {
static jlong nativeInit(JNIEnv *env, jobject obj, jint halRev) {
    ALOGV("nativeInit()");
    AutoMutex _l(gContextMutex);

    auto ctx = new TunerContext();
    ctx->mHalRev = static_cast<HalRevision>(halRev);
    ctx->mNativeCallback = new TunerCallback(env, obj, clientCallback, ctx->mHalRev);

    static_assert(sizeof(jlong) >= sizeof(ctx), "jlong is smaller than a pointer");
    return reinterpret_cast<jlong>(ctx);
@@ -98,12 +97,12 @@ static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
    delete ctx;
}

void setHalTuner(JNIEnv *env, jobject obj, sp<V1_0::ITuner> halTuner) {
void setHalTuner(JNIEnv *env, JavaRef<jobject> const &jTuner, sp<V1_0::ITuner> halTuner) {
    ALOGV("setHalTuner(%p)", halTuner.get());
    ALOGE_IF(halTuner == nullptr, "HAL tuner is a nullptr");

    AutoMutex _l(gContextMutex);
    auto& ctx = getNativeContext(env, obj);
    auto& ctx = getNativeContext(env, jTuner);

    ctx.mHalTuner = halTuner;
    ctx.mHalTuner11 = V1_1::ITuner::castFrom(halTuner).withDefault(nullptr);
@@ -114,21 +113,18 @@ void setHalTuner(JNIEnv *env, jobject obj, sp<V1_0::ITuner> halTuner) {
sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
    AutoMutex _l(gContextMutex);
    auto tuner = getNativeContext(nativeContext).mHalTuner;
    LOG_ALWAYS_FATAL_IF(tuner == nullptr, "HAL tuner not set");
    LOG_ALWAYS_FATAL_IF(tuner == nullptr, "HAL tuner is not open");
    return tuner;
}

sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
    AutoMutex _l(gContextMutex);
    auto tuner = getNativeContext(nativeContext).mHalTuner11;
    LOG_ALWAYS_FATAL_IF(tuner == nullptr, "HAL tuner not set");
    return tuner;
    return getNativeContext(nativeContext).mHalTuner11;
}

sp<ITunerCallback> getNativeCallback(JNIEnv *env, jobject obj) {
    AutoMutex _l(gContextMutex);
    auto& ctx = getNativeContext(env, obj);
    return ctx.mNativeCallback;
sp<ITunerCallback> getNativeCallback(JNIEnv *env, JavaRef<jobject> const &tuner) {
    return TunerCallback::getNativeCallback(env,
            env->GetObjectField(tuner.get(), gjni.Tuner.tunerCallback));
}

Region getRegion(JNIEnv *env, jobject obj) {
@@ -138,16 +134,16 @@ Region getRegion(JNIEnv *env, jobject obj) {
static void nativeClose(JNIEnv *env, jobject obj, jlong nativeContext) {
    AutoMutex _l(gContextMutex);
    auto& ctx = getNativeContext(nativeContext);
    if (ctx.mHalTuner == nullptr) return;
    ALOGI("Closing tuner %p", ctx.mHalTuner.get());
    ctx.mNativeCallback->detach();
    ctx.mHalTuner11 = nullptr;
    ctx.mHalTuner = nullptr;
    ctx.mNativeCallback = nullptr;
}

static void nativeSetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext, jobject config) {
    ALOGV("nativeSetConfiguration()");
    auto halTuner = getHalTuner(nativeContext);
    if (halTuner == nullptr) return;

    Region region_unused;
    BandConfig bandConfigHal = convert::BandConfigToHal(env, config, region_unused);
@@ -159,6 +155,7 @@ static jobject nativeGetConfiguration(JNIEnv *env, jobject obj, jlong nativeCont
        Region region) {
    ALOGV("nativeSetConfiguration()");
    auto halTuner = getHalTuner(nativeContext);
    if (halTuner == nullptr) return nullptr;

    BandConfig halConfig;
    Result halResult;
@@ -177,6 +174,7 @@ static void nativeStep(JNIEnv *env, jobject obj, jlong nativeContext,
        bool directionDown, bool skipSubChannel) {
    ALOGV("nativeStep()");
    auto halTuner = getHalTuner(nativeContext);
    if (halTuner == nullptr) return;

    auto dir = convert::DirectionToHal(directionDown);
    convert::ThrowIfFailed(env, halTuner->step(dir, skipSubChannel));
@@ -186,6 +184,7 @@ static void nativeScan(JNIEnv *env, jobject obj, jlong nativeContext,
        bool directionDown, bool skipSubChannel) {
    ALOGV("nativeScan()");
    auto halTuner = getHalTuner(nativeContext);
    if (halTuner == nullptr) return;

    auto dir = convert::DirectionToHal(directionDown);
    convert::ThrowIfFailed(env, halTuner->scan(dir, skipSubChannel));
@@ -195,6 +194,7 @@ static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext,
        jint channel, jint subChannel) {
    ALOGV("nativeTune(%d, %d)", channel, subChannel);
    auto halTuner = getHalTuner(nativeContext);
    if (halTuner == nullptr) return;

    convert::ThrowIfFailed(env, halTuner->tune(channel, subChannel));
}
@@ -202,6 +202,7 @@ static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext,
static void nativeCancel(JNIEnv *env, jobject obj, jlong nativeContext) {
    ALOGV("nativeCancel()");
    auto halTuner = getHalTuner(nativeContext);
    if (halTuner == nullptr) return;

    convert::ThrowIfFailed(env, halTuner->cancel());
}
@@ -210,6 +211,7 @@ static jobject nativeGetProgramInformation(JNIEnv *env, jobject obj, jlong nativ
    ALOGV("nativeGetProgramInformation()");
    auto halTuner10 = getHalTuner(nativeContext);
    auto halTuner11 = getHalTuner11(nativeContext);
    if (halTuner10 == nullptr) return nullptr;

    V1_1::ProgramInfo halInfo;
    Result halResult;
@@ -234,7 +236,7 @@ static jobject nativeGetProgramInformation(JNIEnv *env, jobject obj, jlong nativ
}

static const JNINativeMethod gTunerMethods[] = {
    { "nativeInit", "(Landroid/hardware/radio/ITunerCallback;I)J", (void*)nativeInit },
    { "nativeInit", "(I)J", (void*)nativeInit },
    { "nativeFinalize", "(J)V", (void*)nativeFinalize },
    { "nativeClose", "(J)V", (void*)nativeClose },
    { "nativeSetConfiguration", "(JLandroid/hardware/radio/RadioManager$BandConfig;)V",
@@ -256,11 +258,13 @@ static const JNINativeMethod gTunerMethods[] = {
void register_android_server_radio_Tuner(JavaVM *vm, JNIEnv *env) {
    using namespace server::radio::Tuner;

    register_android_server_radio_Tuner_TunerCallback(vm, env);
    register_android_server_radio_TunerCallback(vm, env);

    auto tunerClass = FindClassOrDie(env, "com/android/server/radio/Tuner");
    gjni.Tuner.nativeContext = GetFieldIDOrDie(env, tunerClass, "mNativeContext", "J");
    gjni.Tuner.region = GetFieldIDOrDie(env, tunerClass, "mRegion", "I");
    gjni.Tuner.tunerCallback = GetFieldIDOrDie(env, tunerClass, "mTunerCallback",
            "Lcom/android/server/radio/TunerCallback;");

    auto res = jniRegisterNativeMethods(env, "com/android/server/radio/Tuner",
            gTunerMethods, NELEM(gTunerMethods));
Loading