packages/modules/Uwb/service/java/com/android/server/uwb/UwbCountryCode.java

267 lines
10 KiB
Java

/*
* 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.server.uwb;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.ActiveCountryCodeChangedCallback;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.HandlerExecutor;
import com.android.server.uwb.data.UwbUciConstants;
import com.android.server.uwb.jni.NativeUwbManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
/**
* Provide functions for making changes to UWB country code.
* This Country Code is from MCC or phone default setting. This class sends Country Code
* to UWB venodr via the HAL.
*/
public class UwbCountryCode {
private static final String TAG = "UwbCountryCode";
// To be used when there is no country code available.
@VisibleForTesting
public static final String DEFAULT_COUNTRY_CODE = "00";
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
private final Context mContext;
private final Handler mHandler;
private final TelephonyManager mTelephonyManager;
private final NativeUwbManager mNativeUwbManager;
private final UwbInjector mUwbInjector;
private final Set<CountryCodeChangedListener> mListeners = new ArraySet<>();
private String mTelephonyCountryCode = null;
private String mWifiCountryCode = null;
private String mOverrideCountryCode = null;
private String mCountryCode = null;
private String mCountryCodeUpdatedTimestamp = null;
private String mTelephonyCountryTimestamp = null;
private String mWifiCountryTimestamp = null;
public interface CountryCodeChangedListener {
void onCountryCodeChanged(@Nullable String newCountryCode);
}
public UwbCountryCode(
Context context, NativeUwbManager nativeUwbManager, Handler handler,
UwbInjector uwbInjector) {
mContext = context;
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mNativeUwbManager = nativeUwbManager;
mHandler = handler;
mUwbInjector = uwbInjector;
}
private class WifiCountryCodeCallback implements ActiveCountryCodeChangedCallback {
public void onActiveCountryCodeChanged(@NonNull String countryCode) {
setWifiCountryCode(countryCode);
}
public void onCountryCodeInactive() {
setWifiCountryCode("");
}
}
/**
* Initialize the module.
*/
public void initialize() {
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String countryCode = intent.getStringExtra(
TelephonyManager.EXTRA_NETWORK_COUNTRY);
Log.d(TAG, "Country code changed to :" + countryCode);
setTelephonyCountryCode(countryCode);
}
},
new IntentFilter(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED),
null, mHandler);
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
mContext.getSystemService(WifiManager.class).registerActiveCountryCodeChangedCallback(
new HandlerExecutor(mHandler), new WifiCountryCodeCallback());
}
Log.d(TAG, "Default country code from system property is "
+ mUwbInjector.getOemDefaultCountryCode());
setTelephonyCountryCode(mTelephonyManager.getNetworkCountryIso());
// Current Wifi country code update is sent immediately on registration.
}
public void addListener(@NonNull CountryCodeChangedListener listener) {
mListeners.add(listener);
}
private boolean setTelephonyCountryCode(String countryCode) {
if (TextUtils.isEmpty(countryCode)
&& !TextUtils.isEmpty(mTelephonyManager.getNetworkCountryIso())) {
Log.i(TAG, "Skip Telephony CC update to empty because there is "
+ "an available CC from default active SIM");
return false;
}
Log.d(TAG, "Set telephony country code to: " + countryCode);
mTelephonyCountryTimestamp = LocalDateTime.now().format(FORMATTER);
// Empty country code.
if (TextUtils.isEmpty(countryCode)) {
Log.d(TAG, "Received empty telephony country code, reset to default country code");
mTelephonyCountryCode = null;
} else {
mTelephonyCountryCode = countryCode.toUpperCase(Locale.US);
}
return setCountryCode(false);
}
private boolean setWifiCountryCode(String countryCode) {
Log.d(TAG, "Set wifi country code to: " + countryCode);
mWifiCountryTimestamp = LocalDateTime.now().format(FORMATTER);
// Empty country code.
if (TextUtils.isEmpty(countryCode)) {
Log.d(TAG, "Received empty wifi country code, reset to default country code");
mWifiCountryCode = null;
} else {
mWifiCountryCode = countryCode.toUpperCase(Locale.US);
}
return setCountryCode(false);
}
private String pickCountryCode() {
if (mOverrideCountryCode != null) {
return mOverrideCountryCode;
}
if (mTelephonyCountryCode != null) {
return mTelephonyCountryCode;
}
if (mWifiCountryCode != null) {
return mWifiCountryCode;
}
return mUwbInjector.getOemDefaultCountryCode();
}
/**
* Set country code
*
* @param forceUpdate Force update the country code even if it was the same as previously cached
* value.
* @return true if the country code is set successfully, false otherwise.
*/
public boolean setCountryCode(boolean forceUpdate) {
String country = pickCountryCode();
if (country == null) {
Log.i(TAG, "No valid country code, reset to " + DEFAULT_COUNTRY_CODE);
country = DEFAULT_COUNTRY_CODE;
}
if (!forceUpdate && Objects.equals(country, mCountryCode)) {
Log.i(TAG, "Ignoring already set country code: " + country);
return false;
}
Log.d(TAG, "setCountryCode to " + country);
int status = mNativeUwbManager.setCountryCode(country.getBytes(StandardCharsets.UTF_8));
boolean success = (status == UwbUciConstants.STATUS_CODE_OK);
if (!success) {
Log.i(TAG, "Failed to set country code");
return false;
}
mCountryCode = country;
mCountryCodeUpdatedTimestamp = LocalDateTime.now().format(FORMATTER);
for (CountryCodeChangedListener listener : mListeners) {
listener.onCountryCodeChanged(country);
}
return true;
}
/**
* Get country code
*
* @return true if the country code is set successfully, false otherwise.
*/
public String getCountryCode() {
return mCountryCode;
}
/**
* Is this a valid country code
* @param countryCode A 2-Character alphanumeric country code.
* @return true if the countryCode is valid, false otherwise.
*/
public static boolean isValid(String countryCode) {
return countryCode != null && countryCode.length() == 2
&& countryCode.chars().allMatch(Character::isLetterOrDigit);
}
/**
* This call will override any existing country code.
* This is for test purpose only and we should disallow any update from
* telephony in this mode.
* @param countryCode A 2-Character alphanumeric country code.
*/
public synchronized void setOverrideCountryCode(String countryCode) {
if (TextUtils.isEmpty(countryCode)) {
Log.d(TAG, "Fail to override country code because"
+ "the received country code is empty");
return;
}
mOverrideCountryCode = countryCode.toUpperCase(Locale.US);
setCountryCode(true);
}
/**
* This is for clearing the country code previously set through #setOverrideCountryCode() method
*/
public synchronized void clearOverrideCountryCode() {
mOverrideCountryCode = null;
setCountryCode(true);
}
/**
* Method to dump the current state of this UwbCountryCode object.
*/
public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("DefaultCountryCode(system property): "
+ mUwbInjector.getOemDefaultCountryCode());
pw.println("mOverrideCountryCode: " + mOverrideCountryCode);
pw.println("mTelephonyCountryCode: " + mTelephonyCountryCode);
pw.println("mTelephonyCountryTimestamp: " + mTelephonyCountryTimestamp);
pw.println("mWifiCountryCode: " + mWifiCountryCode);
pw.println("mWifiCountryTimestamp: " + mWifiCountryTimestamp);
pw.println("mCountryCode: " + mCountryCode);
pw.println("mCountryCodeUpdatedTimestamp: " + mCountryCodeUpdatedTimestamp);
}
}