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

Commit f13b8419 authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk
Browse files

Implement handling hardware failures of ITuner.

Test: instrumentation tests for other features pass, no tests added
Bug: b/36863239
Change-Id: Iaab9ac06bacc3dcdfde100427653ef1c429d13c6
parent 37d986d1
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