/* * Copyright (C) 2021 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.internal.telephony; import android.annotation.NonNull; import android.os.AsyncResult; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.telephony.Rlog; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collections; import java.util.Set; /** * Provides the capabilities that the Radio Interface supports on the current device. */ public class RadioInterfaceCapabilityController extends Handler { private static final String LOG_TAG = RadioInterfaceCapabilityController.class.getSimpleName(); private static RadioInterfaceCapabilityController sInstance; private final RadioConfig mRadioConfig; private final CommandsInterface mCommandsInterface; private Set mRadioInterfaceCapabilities; private final Object mLockRadioInterfaceCapabilities = new Object(); private static final int EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE = 100; /** * Init method to instantiate the object * Should only be called once. */ public static RadioInterfaceCapabilityController init(final RadioConfig radioConfig, final CommandsInterface commandsInterface) { synchronized (RadioInterfaceCapabilityController.class) { if (sInstance == null) { final HandlerThread handlerThread = new HandlerThread("RHC"); handlerThread.start(); sInstance = new RadioInterfaceCapabilityController(radioConfig, commandsInterface, handlerThread.getLooper()); } else { Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); } return sInstance; } } /** * Static method to get instance. */ public static RadioInterfaceCapabilityController getInstance() { if (sInstance == null) { Log.wtf(LOG_TAG, "getInstance null"); } return sInstance; } @VisibleForTesting public RadioInterfaceCapabilityController(final RadioConfig radioConfig, final CommandsInterface commandsInterface, final Looper looper) { super(looper); mRadioConfig = radioConfig; mCommandsInterface = commandsInterface; register(); } private void requestCapabilities() { if (mRadioInterfaceCapabilities != null) return; mRadioConfig.getHalDeviceCapabilities(obtainMessage( EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE)); } /** * Gets the radio interface capabilities for the device */ @NonNull public Set getCapabilities() { if (mRadioInterfaceCapabilities == null) { // Only incur cost of synchronization block if mRadioInterfaceCapabilities isn't null synchronized (mLockRadioInterfaceCapabilities) { if (mRadioInterfaceCapabilities == null) { requestCapabilities(); try { if (Looper.myLooper() != getLooper()) { mLockRadioInterfaceCapabilities.wait(2000); } } catch (final InterruptedException ignored) { } if (mRadioInterfaceCapabilities == null) { loge("getRadioInterfaceCapabilities: Radio Capabilities not " + "loaded in time"); return new ArraySet<>(); } } } } return mRadioInterfaceCapabilities; } private void setupCapabilities(final @NonNull AsyncResult ar) { if (mRadioInterfaceCapabilities == null) { synchronized (mLockRadioInterfaceCapabilities) { if (mRadioInterfaceCapabilities == null) { if (ar.exception != null) { loge("setupRadioInterfaceCapabilities: " + ar.exception); } if (ar.result == null) { loge("setupRadioInterfaceCapabilities: ar.result is null"); return; } log("setupRadioInterfaceCapabilities: " + "mRadioInterfaceCapabilities now setup"); mRadioInterfaceCapabilities = Collections.unmodifiableSet((Set) ar.result); if (mRadioInterfaceCapabilities != null) { unregister(); } } mLockRadioInterfaceCapabilities.notify(); } } } private void register() { // There is no radio HAL, capabilities are irrelevant in this case. if (mCommandsInterface == null) { mRadioInterfaceCapabilities = Collections.unmodifiableSet(new ArraySet<>()); return; } mCommandsInterface.registerForAvailable(this, Phone.EVENT_RADIO_AVAILABLE, null); } private void unregister() { mCommandsInterface.unregisterForAvailable(this); } @Override public void handleMessage(final Message msg) { switch (msg.what) { case Phone.EVENT_RADIO_AVAILABLE: case Phone.EVENT_RADIO_ON: requestCapabilities(); break; case EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE: setupCapabilities((AsyncResult) msg.obj); break; } } /** * Dump the fields of the instance */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("mRadioConfig=" + mRadioConfig); } private static void log(final String s) { Rlog.d(LOG_TAG, s); } private static void loge(final String s) { Rlog.e(LOG_TAG, s); } }