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

Commit 5a91648a authored by Daniel Bright's avatar Daniel Bright
Browse files

Initalize Radio Hal Capabilities before Phone

A component of Phone needs access to radio hal capabilities.
This adds two requirements on how the capabilities get loaded:
1. They need to be loaded before PhoneFactory#makeDefaultPhone
2. They need to be loaded on a thread other than the main thread
As a result, the loading code is taken out of
PhoneConfigurationManager and into its own class which uses a
thread other than main.

Test: Temporarily added radio hal capabilities check
within IccPhoneBookInterfaceManager and checked logs.
Bug: 181462353
Merged-In: I5a74c3195e9c7a632c2401024994bcd86788c8f6
Change-Id: I6a51039ff0bacb57f9704438639e0289087a8f9d
parent ab604a06
Loading
Loading
Loading
Loading
+0 −56
Original line number Diff line number Diff line
@@ -19,12 +19,10 @@ package com.android.internal.telephony;
import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
import static android.telephony.TelephonyManager.EXTRA_ACTIVE_SIM_SUPPORTED_COUNT;

import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RegistrantList;
@@ -33,7 +31,6 @@ import android.sysprop.TelephonyProperties;
import android.telephony.PhoneCapability;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
@@ -42,7 +39,6 @@ import com.android.telephony.Rlog;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

/**
 * This class manages phone's configuration which defines the potential capability (static) of the
@@ -60,7 +56,6 @@ public class PhoneConfigurationManager {
    private static final int EVENT_GET_MODEM_STATUS = 101;
    private static final int EVENT_GET_MODEM_STATUS_DONE = 102;
    private static final int EVENT_GET_PHONE_CAPABILITY_DONE = 103;
    private static final int EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE = 104;

    private static PhoneConfigurationManager sInstance = null;
    private final Context mContext;
@@ -74,8 +69,6 @@ public class PhoneConfigurationManager {
    private MockableInterface mMi = new MockableInterface();
    private TelephonyManager mTelephonyManager;
    private static final RegistrantList sMultiSimConfigChangeRegistrants = new RegistrantList();
    private Set<String> mRadioInterfaceCapabilities;
    private final Object mLockRadioInterfaceCapabilities = new Object();

    /**
     * Init method to instantiate the object
@@ -164,7 +157,6 @@ public class PhoneConfigurationManager {
                                + "No phone object provided for event " + msg.what);
                    }
                    getStaticPhoneCapability();
                    getRadioInterfaceCapabilities();
                    break;
                case EVENT_SWITCH_DSDS_CONFIG_DONE:
                    ar = (AsyncResult) msg.obj;
@@ -195,9 +187,6 @@ public class PhoneConfigurationManager {
                        log(msg.what + " failure. Not getting phone capability." + ar.exception);
                    }
                    break;
                case EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE:
                    setupRadioInterfaceCapabilities((AsyncResult) msg.obj);
                    break;
            }
        }
    }
@@ -315,51 +304,6 @@ public class PhoneConfigurationManager {
        return mStaticCapability;
    }

    /**
     * Gets the radio interface capabilities for the device
     */
    @NonNull
    public synchronized Set<String> getRadioInterfaceCapabilities() {
        if (mRadioInterfaceCapabilities == null) {
            // Only incur cost of synchronization block if mRadioInterfaceCapabilities isn't null
            synchronized (mLockRadioInterfaceCapabilities) {
                if (mRadioInterfaceCapabilities == null) {
                    mRadioConfig.getHalDeviceCapabilities(
                            mHandler.obtainMessage(EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE));
                    try {
                        if (Looper.myLooper() == this.mHandler.getLooper()) {
                            //Expected if this is called after the radio just turns on
                            log("getRadioInterfaceCapabilities: myLoop == handler.getLooper "
                                    + "returning non-available capabilities.");
                        } else {
                            mLockRadioInterfaceCapabilities.wait(2000);
                        }
                    } catch (InterruptedException e) {
                    }
                }
            }
        }

        if (mRadioInterfaceCapabilities == null) return new ArraySet<>();
        return mRadioInterfaceCapabilities;
    }

    private void setupRadioInterfaceCapabilities(@NonNull AsyncResult ar) {
        if (mRadioInterfaceCapabilities == null) {
            synchronized (mLockRadioInterfaceCapabilities) {
                if (mRadioInterfaceCapabilities == null) {
                    if (ar.exception != null) {
                        loge("setupRadioInterfaceCapabilities: " + ar.exception);
                    }
                    log("setupRadioInterfaceCapabilities: "
                            + "mRadioInterfaceCapabilities now setup");
                    mRadioInterfaceCapabilities = (Set<String>) ar.result;
                }
                mLockRadioInterfaceCapabilities.notify();
            }
        }
    }

    /**
     * get configuration related status of each phone.
     */
+14 −4
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ public class PhoneFactory {

    static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>();
    private static MetricsCollector sMetricsCollector;
    private static RadioInterfaceCapabilityController sRadioHalCapabilities;

    //***** Class Methods

@@ -175,10 +176,19 @@ public class PhoneFactory {
                            RadioAccessFamily.getRafFromNetworkType(networkModes[i]),
                            cdmaSubscription, i);
                }
                HalVersion radioHalVersion;
                if (numPhones > 0) radioHalVersion = sCommandsInterfaces[0].getHalVersion();
                else radioHalVersion = HalVersion.UNKNOWN;
                RadioConfig.make(context, radioHalVersion);


                if (numPhones > 0) {
                    final RadioConfig radioConfig = RadioConfig.make(context,
                            sCommandsInterfaces[0].getHalVersion());
                    sRadioHalCapabilities = RadioInterfaceCapabilityController.init(radioConfig,
                            sCommandsInterfaces[0]);
                } else {
                    // There is no command interface to go off of
                    final RadioConfig radioConfig = RadioConfig.make(context, HalVersion.UNKNOWN);
                    sRadioHalCapabilities = RadioInterfaceCapabilityController.init(
                            radioConfig, null);
                }

                // Instantiate UiccController so that all other classes can just
                // call getInstance()
+181 −0
Original line number Diff line number Diff line
/*
 * 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.os.storage.StorageManager;
import android.util.ArraySet;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.telephony.Rlog;

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 final boolean mRegisterForOn;
    private Set<String> 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;
        mRegisterForOn = StorageManager.inCryptKeeperBounce();
        register();
    }

    /**
     * Gets the radio interface capabilities for the device
     */
    @NonNull
    public Set<String> getCapabilities() {
        if (mRadioInterfaceCapabilities == null) {
            // Only incur cost of synchronization block if mRadioInterfaceCapabilities isn't null
            synchronized (mLockRadioInterfaceCapabilities) {
                if (mRadioInterfaceCapabilities == null) {
                    mRadioConfig.getHalDeviceCapabilities(
                            obtainMessage(EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE));
                    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);
                    }
                    log("setupRadioInterfaceCapabilities: "
                            + "mRadioInterfaceCapabilities now setup");
                    mRadioInterfaceCapabilities =
                            Collections.unmodifiableSet((Set<String>) 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;
        }

        if (mRegisterForOn) {
            mCommandsInterface.registerForOn(this, Phone.EVENT_RADIO_ON, null);
        } else {
            mCommandsInterface.registerForAvailable(this, Phone.EVENT_RADIO_AVAILABLE, null);
        }
    }

    private void unregister() {
        if (mRegisterForOn) {
            mCommandsInterface.unregisterForOn(this);
        } else {
            mCommandsInterface.unregisterForAvailable(this);
        }
    }

    @Override
    public void handleMessage(final Message msg) {
        switch (msg.what) {
            case Phone.EVENT_RADIO_AVAILABLE:
            case Phone.EVENT_RADIO_ON:
                getCapabilities();
                break;
            case EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE:
                setupCapabilities((AsyncResult) msg.obj);
                break;
        }
    }

    private static void log(final String s) {
        Rlog.d(LOG_TAG, s);
    }

    private static void loge(final String s) {
        Rlog.e(LOG_TAG, s);
    }
}
+111 −0
Original line number Diff line number Diff line
/*
 * 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 static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;

import java.util.HashSet;
import java.util.Set;

@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class RadioInterfaceCapabilityControllerTest extends TelephonyTest {
    @Mock
    RadioConfig mMockRadioConfig;

    @Mock
    CommandsInterface mMockCommandsInterface;

    @Before
    public void setUp() throws Exception {
        super.setUp(getClass().getSimpleName());
    }

    @Test
    public void testRadioInterfaceCapabilities() {
        final RadioInterfaceCapabilityController capabilities =
                new RadioInterfaceCapabilityController(mMockRadioConfig, mMockCommandsInterface,
                        mTestableLooper.getLooper());

        // The capabilities to test for
        final Set<String> capabilitySet = new HashSet<>();
        capabilitySet.add(TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE);

        registerForRadioAvailable();
        getHalDeviceCapabilities(capabilitySet);

        // Test for the capabilities
        assertEquals(1, capabilities.getCapabilities().size());
        assertTrue(capabilities.getCapabilities()
                .contains(TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE));
    }

    private void registerForRadioAvailable() {
        // Capture radio avaialble
        final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
        final ArgumentCaptor<Integer> whatCaptor = ArgumentCaptor.forClass(Integer.class);
        final ArgumentCaptor<Object> objCaptor = ArgumentCaptor.forClass(Object.class);
        verify(mMockCommandsInterface, times(1)).registerForAvailable(
                handlerCaptor.capture(), whatCaptor.capture(), objCaptor.capture());

        // Send Message back through handler
        final Message m = Message.obtain(
                handlerCaptor.getValue(), whatCaptor.getValue(), objCaptor.getValue());
        m.sendToTarget();
        processAllMessages();
    }

    private void getHalDeviceCapabilities(final Set<String> capabilitySet) {
        // Capture Message when the capabilities are requested
        final ArgumentCaptor<Message> deviceCapsMessage = ArgumentCaptor.forClass(Message.class);
        verify(mMockRadioConfig, times(1))
                .getHalDeviceCapabilities(deviceCapsMessage.capture());

        // Send Message back through handler
        final Message m = deviceCapsMessage.getValue();
        AsyncResult.forMessage(m, capabilitySet, null);
        m.sendToTarget();
        processAllMessages();
    }

    @Test
    public void testEmptyRadioInterfaceCapabilities() {
        final RadioInterfaceCapabilityController capabilities =
                new RadioInterfaceCapabilityController(mMockRadioConfig, null,
                        mTestableLooper.getLooper());

        // Test for the capabilities
        assertEquals(0, capabilities.getCapabilities().size());
    }
}