1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony; 18 19 import android.annotation.NonNull; 20 import android.os.AsyncResult; 21 import android.os.Handler; 22 import android.os.HandlerThread; 23 import android.os.Looper; 24 import android.os.Message; 25 import android.util.ArraySet; 26 import android.util.Log; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.telephony.Rlog; 30 31 import java.util.Collections; 32 import java.util.Set; 33 34 /** 35 * Provides the capabilities that the Radio Interface supports on the current device. 36 */ 37 public class RadioInterfaceCapabilityController extends Handler { 38 private static final String LOG_TAG = 39 RadioInterfaceCapabilityController.class.getSimpleName(); 40 41 private static RadioInterfaceCapabilityController sInstance; 42 private final RadioConfig mRadioConfig; 43 private final CommandsInterface mCommandsInterface; 44 private Set<String> mRadioInterfaceCapabilities; 45 private final Object mLockRadioInterfaceCapabilities = new Object(); 46 private static final int EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE = 100; 47 48 /** 49 * Init method to instantiate the object 50 * Should only be called once. 51 */ init(final RadioConfig radioConfig, final CommandsInterface commandsInterface)52 public static RadioInterfaceCapabilityController init(final RadioConfig radioConfig, 53 final CommandsInterface commandsInterface) { 54 synchronized (RadioInterfaceCapabilityController.class) { 55 if (sInstance == null) { 56 final HandlerThread handlerThread = new HandlerThread("RHC"); 57 handlerThread.start(); 58 sInstance = new RadioInterfaceCapabilityController(radioConfig, commandsInterface, 59 handlerThread.getLooper()); 60 } else { 61 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 62 } 63 return sInstance; 64 } 65 } 66 67 /** 68 * Static method to get instance. 69 */ getInstance()70 public static RadioInterfaceCapabilityController getInstance() { 71 if (sInstance == null) { 72 Log.wtf(LOG_TAG, "getInstance null"); 73 } 74 75 return sInstance; 76 } 77 78 @VisibleForTesting RadioInterfaceCapabilityController(final RadioConfig radioConfig, final CommandsInterface commandsInterface, final Looper looper)79 public RadioInterfaceCapabilityController(final RadioConfig radioConfig, 80 final CommandsInterface commandsInterface, final Looper looper) { 81 super(looper); 82 mRadioConfig = radioConfig; 83 mCommandsInterface = commandsInterface; 84 register(); 85 } 86 87 /** 88 * Gets the radio interface capabilities for the device 89 */ 90 @NonNull getCapabilities()91 public Set<String> getCapabilities() { 92 if (mRadioInterfaceCapabilities == null) { 93 // Only incur cost of synchronization block if mRadioInterfaceCapabilities isn't null 94 synchronized (mLockRadioInterfaceCapabilities) { 95 if (mRadioInterfaceCapabilities == null) { 96 mRadioConfig.getHalDeviceCapabilities( 97 obtainMessage(EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE)); 98 try { 99 if (Looper.myLooper() != getLooper()) { 100 mLockRadioInterfaceCapabilities.wait(2000); 101 } 102 } catch (final InterruptedException ignored) { 103 } 104 105 if (mRadioInterfaceCapabilities == null) { 106 loge("getRadioInterfaceCapabilities: Radio Capabilities not " 107 + "loaded in time"); 108 return new ArraySet<>(); 109 } 110 } 111 } 112 } 113 return mRadioInterfaceCapabilities; 114 } 115 setupCapabilities(final @NonNull AsyncResult ar)116 private void setupCapabilities(final @NonNull AsyncResult ar) { 117 if (mRadioInterfaceCapabilities == null) { 118 synchronized (mLockRadioInterfaceCapabilities) { 119 if (mRadioInterfaceCapabilities == null) { 120 if (ar.exception != null) { 121 loge("setupRadioInterfaceCapabilities: " + ar.exception); 122 } 123 if (ar.result == null) { 124 loge("setupRadioInterfaceCapabilities: ar.result is null"); 125 return; 126 } 127 log("setupRadioInterfaceCapabilities: " 128 + "mRadioInterfaceCapabilities now setup"); 129 mRadioInterfaceCapabilities = 130 Collections.unmodifiableSet((Set<String>) ar.result); 131 if (mRadioInterfaceCapabilities != null) { 132 unregister(); 133 } 134 } 135 mLockRadioInterfaceCapabilities.notify(); 136 } 137 } 138 } 139 register()140 private void register() { 141 // There is no radio HAL, capabilities are irrelevant in this case. 142 if (mCommandsInterface == null) { 143 mRadioInterfaceCapabilities = Collections.unmodifiableSet(new ArraySet<>()); 144 return; 145 } 146 147 mCommandsInterface.registerForAvailable(this, Phone.EVENT_RADIO_AVAILABLE, null); 148 } 149 unregister()150 private void unregister() { 151 mCommandsInterface.unregisterForAvailable(this); 152 } 153 154 @Override handleMessage(final Message msg)155 public void handleMessage(final Message msg) { 156 switch (msg.what) { 157 case Phone.EVENT_RADIO_AVAILABLE: 158 case Phone.EVENT_RADIO_ON: 159 getCapabilities(); 160 break; 161 case EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE: 162 setupCapabilities((AsyncResult) msg.obj); 163 break; 164 } 165 } 166 log(final String s)167 private static void log(final String s) { 168 Rlog.d(LOG_TAG, s); 169 } 170 loge(final String s)171 private static void loge(final String s) { 172 Rlog.e(LOG_TAG, s); 173 } 174 } 175