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