1 /* 2 * Copyright (C) 2022 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 android.net.wifi; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.telephony.SubscriptionManager; 22 import android.telephony.TelephonyManager; 23 import android.util.Log; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 27 /** 28 * Provide a wrapper to reading string overlay resources for WiFi. 29 * 30 * Specifically intended to provide a mechanism to store and read carrier-specific translatable 31 * string overlays. Carrier-specific (MVNO) overlays are not supported - but Carrier Configurations 32 * which do support MVNOs do not support translatable strings. 33 * 34 * Structure: 35 * <string name="wifi_eap_error_message_code_32760">EAP authentication error 32760</string> 36 * <string-array name=”wifi_eap_error_message_code_32760_carrier_overrides”> 37 * <item><xliff:g id="carrier_id_prefix">:::1234:::</xliff:g>EAP error 32760 for carrier 1234</item> 38 * <item><xliff:g id="carrier_id_prefix">:::5678:::</xliff:g>EAP error 32760 for carrier 5678</item> 39 * … 40 * </string-array> 41 * 42 * The WiFi-stack specific solution is to store the strings in the general name-space with a known 43 * prefix. 44 * 45 * @hide 46 */ 47 public class WifiStringResourceWrapper { 48 private static final String TAG = "WifiStringResourceWrapper"; 49 50 private final WifiContext mContext; 51 private final int mSubId; 52 private final int mCarrierId; 53 54 private Resources mResources; 55 private final String mCarrierIdPrefix; 56 57 @VisibleForTesting 58 static final String CARRIER_ID_RESOURCE_NAME_SUFFIX = "_carrier_overrides"; 59 60 @VisibleForTesting 61 static final String CARRIER_ID_RESOURCE_SEPARATOR = ":::"; 62 63 /** 64 * @param context a WifiContext 65 * @param subId the sub ID to use for all the resources (overlays or carrier ID) 66 */ WifiStringResourceWrapper(WifiContext context, int subId, int carrierId)67 public WifiStringResourceWrapper(WifiContext context, int subId, int carrierId) { 68 mContext = context; 69 mSubId = subId; 70 mCarrierId = carrierId; 71 mCarrierIdPrefix = 72 CARRIER_ID_RESOURCE_SEPARATOR + mCarrierId + CARRIER_ID_RESOURCE_SEPARATOR; 73 } 74 75 /** 76 * Returns the string corresponding to the resource ID - or null if no resources exist. 77 */ getString(String name, Object... args)78 public String getString(String name, Object... args) { 79 if (getResourcesForSubId() == null) return null; 80 int resourceId = mResources.getIdentifier(name, "string", 81 mContext.getWifiOverlayApkPkgName()); 82 if (resourceId == 0) return null; 83 84 // check if there's a carrier-specific override array 85 if (mCarrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 86 String carrierOverrideString = getCarrierOverrideString(name, args); 87 if (carrierOverrideString != null) { 88 return carrierOverrideString; 89 } 90 } 91 92 try { 93 return mResources.getString(resourceId, args); 94 } catch (java.util.IllegalFormatException e) { 95 Log.e(TAG, "Resource formatting error - '" + name + "' - " + e); 96 return null; 97 } 98 } 99 100 /** 101 * Returns the int corresponding to the resource ID - or the default value if no resources 102 * exist. 103 */ getInt(String name, int defaultValue)104 public int getInt(String name, int defaultValue) { 105 if (getResourcesForSubId() == null) return defaultValue; 106 int resourceId = mResources.getIdentifier(name, "integer", 107 mContext.getWifiOverlayApkPkgName()); 108 if (resourceId == 0) return defaultValue; 109 110 // check if there's a carrier-specific override array 111 if (mCarrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 112 String carrierOverrideString = getCarrierOverrideString(name); 113 if (carrierOverrideString != null) { 114 try { 115 return Integer.parseInt(carrierOverrideString); 116 } catch (Exception e) { 117 Log.e(TAG, "Failed to parse String into int. String=" + carrierOverrideString); 118 } 119 } 120 } 121 return mResources.getInteger(resourceId); 122 } 123 124 /** 125 * Returns the boolean corresponding to the resource ID - or the default value if no resources 126 * exist. 127 */ getBoolean(String name, boolean defaultValue)128 public boolean getBoolean(String name, boolean defaultValue) { 129 if (getResourcesForSubId() == null) return defaultValue; 130 int resourceId = mResources.getIdentifier(name, "bool", 131 mContext.getWifiOverlayApkPkgName()); 132 if (resourceId == 0) return defaultValue; 133 134 // check if there's a carrier-specific override array 135 if (mCarrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 136 String carrierOverrideString = getCarrierOverrideString(name); 137 if (carrierOverrideString != null) { 138 try { 139 return Boolean.parseBoolean(carrierOverrideString); 140 } catch (Exception e) { 141 Log.e(TAG, "Failed to parse String into boolean. String=" 142 + carrierOverrideString); 143 } 144 } 145 } 146 return mResources.getBoolean(resourceId); 147 } 148 149 /** 150 * Return the String resource override by the carrier, or null if no override is found. 151 */ getCarrierOverrideString(String name, Object... args)152 private String getCarrierOverrideString(String name, Object... args) { 153 int arrayResourceId = mResources.getIdentifier(name + CARRIER_ID_RESOURCE_NAME_SUFFIX, 154 "array", mContext.getWifiOverlayApkPkgName()); 155 if (arrayResourceId != 0) { 156 String[] carrierIdOverlays = mResources.getStringArray(arrayResourceId); 157 // check for the :::carrier-id::: prefix and if exists format and return it 158 for (String carrierIdOverlay : carrierIdOverlays) { 159 if (carrierIdOverlay.indexOf(mCarrierIdPrefix) != 0) continue; 160 try { 161 return String.format(carrierIdOverlay.substring(mCarrierIdPrefix.length()), 162 args); 163 } catch (java.util.IllegalFormatException e) { 164 Log.e(TAG, "Resource formatting error - '" + name + "' - " + e); 165 return null; 166 } 167 } 168 } 169 return null; 170 } 171 172 /** 173 * Returns the resources from the given context for the MCC/MNC 174 * associated with the subscription. 175 */ getResourcesForSubId()176 private Resources getResourcesForSubId() { 177 if (mResources != null) { 178 return mResources; 179 } 180 Context context = mContext.getResourcesApkContext(); 181 if (context == null) { 182 return null; 183 } 184 mResources = SubscriptionManager.getResourcesForSubId(context, mSubId); 185 return mResources; 186 } 187 } 188