blob: 8d634637c4104b423bbe677983fc231411177cce [file] [log] [blame]
hyosun873e6dc2021-12-22 14:56:03 +00001/*
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
17package com.android.phone;
18
19import android.content.Context;
20import android.content.SharedPreferences;
21import android.os.PersistableBundle;
22import android.preference.PreferenceManager;
23import android.telephony.ims.ProvisioningManager;
24import android.telephony.ims.feature.ImsFeature;
25import android.telephony.ims.feature.MmTelFeature;
26import android.telephony.ims.stub.ImsRegistrationImplBase;
27import android.util.Log;
28import android.util.SparseArray;
29
30import com.android.internal.annotations.VisibleForTesting;
31
32import java.io.File;
33import java.io.FileInputStream;
34import java.io.FileNotFoundException;
35import java.io.FileOutputStream;
36import java.io.IOException;
37
38/**
39 * Provides a function to set/get Ims feature provisioning status in storage.
40 */
41public class ImsProvisioningLoader {
42 private static final String LOG_TAG = ImsProvisioningLoader.class.getSimpleName();
43
44 public static final int STATUS_NOT_SET = -1;
45 public static final int STATUS_NOT_PROVISIONED =
46 ProvisioningManager.PROVISIONING_VALUE_DISABLED;
47 public static final int STATUS_PROVISIONED =
48 ProvisioningManager.PROVISIONING_VALUE_ENABLED;
49
50 public static final int IMS_FEATURE_MMTEL = ImsFeature.FEATURE_MMTEL;
51 public static final int IMS_FEATURE_RCS = ImsFeature.FEATURE_RCS;
52
53 private static final String PROVISIONING_FILE_NAME_PREF = "imsprovisioningstatus_";
54 private static final String PREF_PROVISION_IMS_MMTEL_PREFIX = "provision_ims_mmtel_";
55
56 private Context mContext;
57 private SharedPreferences mTelephonySharedPreferences;
58 // key : sub Id, value : read from sub Id's xml and it's in-memory cache
59 private SparseArray<PersistableBundle> mSubIdBundleArray = new SparseArray<>();
60 private final Object mLock = new Object();
61
62 public ImsProvisioningLoader(Context context) {
63 mContext = context;
64 mTelephonySharedPreferences =
65 PreferenceManager.getDefaultSharedPreferences(context);
66 }
67
68 /**
69 * Get Ims feature provisioned status in storage
70 */
71 public int getProvisioningStatus(int subId, @ImsFeature.FeatureType int imsFeature,
72 int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
73 initCache(subId);
74 return getImsProvisioningStatus(subId, imsFeature, tech,
75 capability);
76 }
77
78 /**
79 * Set Ims feature provisioned status in storage
80 */
81 public boolean setProvisioningStatus(int subId, @ImsFeature.FeatureType int imsFeature,
82 int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech,
83 boolean isProvisioned) {
84 initCache(subId);
85 return setImsFeatureProvisioning(subId, imsFeature, tech, capability,
86 isProvisioned);
87 }
88
89 private boolean isFileExist(int subId) {
90 File file = new File(mContext.getFilesDir(), getFileName(subId));
91 return file.exists();
92 }
93
94 private void initCache(int subId) {
95 synchronized (mLock) {
96 PersistableBundle subIdBundle = mSubIdBundleArray.get(subId, null);
97 if (subIdBundle != null) {
98 // initCache() has already been called for the subId
99 return;
100 }
101 if (isFileExist(subId)) {
102 subIdBundle = readSubIdBundleFromXml(subId);
103 } else {
104 // It should read the MMTEL capability cache as part of shared prefs and migrate
105 // over any configs for UT.
106 final int[] regTech = {ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
107 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
108 ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM,
109 ImsRegistrationImplBase.REGISTRATION_TECH_NR};
110 subIdBundle = new PersistableBundle();
111 for (int tech : regTech) {
112 int UtProvisioningStatus = getUTProvisioningStatus(subId, tech);
joonhunshinf45e45b2022-02-03 06:37:55 +0000113 logd("check UT provisioning status " + UtProvisioningStatus);
114
hyosun873e6dc2021-12-22 14:56:03 +0000115 if (STATUS_PROVISIONED == UtProvisioningStatus) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700116 setProvisioningStatusToSubIdBundle(subId, ImsFeature.FEATURE_MMTEL, tech,
hyosun873e6dc2021-12-22 14:56:03 +0000117 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, subIdBundle,
118 UtProvisioningStatus);
119 }
120 }
121 saveSubIdBundleToXml(subId, subIdBundle);
122 }
123 mSubIdBundleArray.put(subId, subIdBundle);
124 }
125 }
126
127 private int getImsProvisioningStatus(int subId, int imsFeature, int tech, int capability) {
128 PersistableBundle subIdBundle = null;
129 synchronized (mLock) {
130 subIdBundle = mSubIdBundleArray.get(subId, null);
131 }
132
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700133 return getProvisioningStatusFromSubIdBundle(subId, imsFeature, tech,
hyosun873e6dc2021-12-22 14:56:03 +0000134 capability, subIdBundle);
135 }
136
137 private boolean setImsFeatureProvisioning(int subId, int imsFeature, int tech, int capability,
138 boolean isProvisioned) {
139 synchronized (mLock) {
140 int preValue = getImsProvisioningStatus(subId, imsFeature, tech, capability);
141 int newValue = isProvisioned ? STATUS_PROVISIONED : STATUS_NOT_PROVISIONED;
142 if (preValue == newValue) {
143 logd("already stored provisioning status " + isProvisioned + " ImsFeature "
144 + imsFeature + " tech " + tech + " capa " + capability);
145 return false;
146 }
147
148 PersistableBundle subIdBundle = mSubIdBundleArray.get(subId, null);
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700149 setProvisioningStatusToSubIdBundle(subId, imsFeature, tech, capability, subIdBundle,
hyosun873e6dc2021-12-22 14:56:03 +0000150 newValue);
151 saveSubIdBundleToXml(subId, subIdBundle);
152 }
153 return true;
154 }
155
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700156 private int getProvisioningStatusFromSubIdBundle(int subId, int imsFeature, int tech,
hyosun873e6dc2021-12-22 14:56:03 +0000157 int capability, PersistableBundle subIdBundle) {
158 // If it doesn't exist in xml, return STATUS_NOT_SET
159 if (subIdBundle == null || subIdBundle.isEmpty()) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700160 logd("getProvisioningStatusFromSubIdBundle", subId, "xml is empty");
hyosun873e6dc2021-12-22 14:56:03 +0000161 return STATUS_NOT_SET;
162 }
163
164 PersistableBundle regTechBundle = subIdBundle.getPersistableBundle(
165 String.valueOf(imsFeature));
166 if (regTechBundle == null) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700167 logd("getProvisioningStatusFromSubIdBundle", subId,
168 "ImsFeature " + imsFeature + " does not exist in xml");
hyosun873e6dc2021-12-22 14:56:03 +0000169 return STATUS_NOT_SET;
170 }
171
172 PersistableBundle capabilityBundle = regTechBundle.getPersistableBundle(
173 String.valueOf(tech));
174 if (capabilityBundle == null) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700175 logd("getProvisioningStatusFromSubIdBundle", subId,
176 "RegistrationTech " + tech + " does not exist in xml");
hyosun873e6dc2021-12-22 14:56:03 +0000177 return STATUS_NOT_SET;
178 }
179
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700180 return getIntValueFromBundle(subId, tech, String.valueOf(capability), capabilityBundle);
hyosun873e6dc2021-12-22 14:56:03 +0000181 }
182
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700183 private void setProvisioningStatusToSubIdBundle(int subId, int imsFeature, int tech,
hyosun873e6dc2021-12-22 14:56:03 +0000184 int capability, PersistableBundle subIdBundle, int newStatus) {
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700185 logd("setProvisioningStatusToSubIdBundle", subId, "set provisioning status " + newStatus
186 + " ImsFeature " + imsFeature + " tech " + tech + " capa " + capability);
joonhunshinf45e45b2022-02-03 06:37:55 +0000187
hyosun873e6dc2021-12-22 14:56:03 +0000188 PersistableBundle regTechBundle = subIdBundle.getPersistableBundle(
189 String.valueOf(imsFeature));
190 if (regTechBundle == null) {
191 regTechBundle = new PersistableBundle();
192 subIdBundle.putPersistableBundle(String.valueOf(imsFeature), regTechBundle);
193 }
194
195 PersistableBundle capabilityBundle = regTechBundle.getPersistableBundle(
196 String.valueOf(tech));
197 if (capabilityBundle == null) {
198 capabilityBundle = new PersistableBundle();
199 regTechBundle.putPersistableBundle(String.valueOf(tech), capabilityBundle);
200 }
201
202 capabilityBundle.putInt(String.valueOf(capability), newStatus);
203 }
204
205 // Default value is STATUS_NOT_SET
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700206 private int getIntValueFromBundle(int subId, int tech, String key, PersistableBundle bundle) {
joonhunshinf45e45b2022-02-03 06:37:55 +0000207 int value = bundle.getInt(key, STATUS_NOT_SET);
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700208 logd("getIntValueFromBundle", subId,
209 "Cache hit, tech=" + tech + " capability=" + key + ": returning " + value);
joonhunshinf45e45b2022-02-03 06:37:55 +0000210 return value;
hyosun873e6dc2021-12-22 14:56:03 +0000211 }
212
213 // Return subIdBundle from imsprovisioningstatus_{subId}.xml
214 private PersistableBundle readSubIdBundleFromXml(int subId) {
215 String fileName = getFileName(subId);
216
217 PersistableBundle subIdBundles = new PersistableBundle();
218 File file = null;
219 FileInputStream inFile = null;
220 synchronized (mLock) {
221 try {
222 file = new File(mContext.getFilesDir(), fileName);
223 inFile = new FileInputStream(file);
224 subIdBundles = PersistableBundle.readFromStream(inFile);
225 inFile.close();
226 } catch (FileNotFoundException e) {
227 logd(e.toString());
228 } catch (IOException e) {
229 loge(e.toString());
230 } catch (RuntimeException e) {
231 loge(e.toString());
232 }
233 }
234
235 return subIdBundles;
236 }
237
238 private void saveSubIdBundleToXml(int subId, PersistableBundle subIdBundle) {
239 String fileName = getFileName(subId);
240
241 if (subIdBundle == null || subIdBundle.isEmpty()) {
242 logd("subIdBundle is empty");
243 return;
244 }
245
246 FileOutputStream outFile = null;
247 synchronized (mLock) {
248 try {
249 outFile = new FileOutputStream(new File(mContext.getFilesDir(), fileName));
250 subIdBundle.writeToStream(outFile);
251 outFile.flush();
252 outFile.close();
253 } catch (IOException e) {
254 loge(e.toString());
255 } catch (RuntimeException e) {
256 loge(e.toString());
257 }
258 }
259 }
260
261 private int getUTProvisioningStatus(int subId, int tech) {
262 return getMmTelCapabilityProvisioningBitfield(subId, tech) > 0 ? STATUS_PROVISIONED
263 : STATUS_NOT_SET;
264 }
265
266 /**
267 * @return the bitfield containing the MmTel provisioning for the provided subscription and
268 * technology. The bitfield should mirror the bitfield defined by
269 * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
270 */
271 private int getMmTelCapabilityProvisioningBitfield(int subId, int tech) {
272 String key = getMmTelProvisioningKey(subId, tech);
273 // Default is no capabilities are provisioned.
274 return mTelephonySharedPreferences.getInt(key, 0 /*default*/);
275 }
276
277 private String getMmTelProvisioningKey(int subId, int tech) {
278 // Resulting key is provision_ims_mmtel_{subId}_{tech}
279 return PREF_PROVISION_IMS_MMTEL_PREFIX + subId + "_" + tech;
280 }
281
282 private String getFileName(int subId) {
283 // Resulting name is imsprovisioningstatus_{subId}.xml
284 return PROVISIONING_FILE_NAME_PREF + subId + ".xml";
285 }
286
287 @VisibleForTesting
288 void clear() {
289 synchronized (mLock) {
290 mSubIdBundleArray.clear();
291 }
292 }
293
294 @VisibleForTesting
295 void setProvisioningToXml(int subId, PersistableBundle subIdBundle,
296 String[] infoArray) {
297 for (String info : infoArray) {
298 String[] paramArray = info.split(",");
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700299 setProvisioningStatusToSubIdBundle(subId, Integer.valueOf(paramArray[0]),
hyosun873e6dc2021-12-22 14:56:03 +0000300 Integer.valueOf(paramArray[1]), Integer.valueOf(paramArray[2]),
301 subIdBundle, Integer.valueOf(paramArray[3]));
302 }
303 saveSubIdBundleToXml(subId, subIdBundle);
304 }
305
306 private void loge(String contents) {
307 Log.e(LOG_TAG, contents);
308 }
309
Brad Ebinger0df4fdf2024-07-19 17:26:12 -0700310 private void logd(String prefix, int subId, String contents) {
311 Log.d(LOG_TAG, prefix + "[" + subId + "]: " + contents);
312 }
313
hyosun873e6dc2021-12-22 14:56:03 +0000314 private void logd(String contents) {
315 Log.d(LOG_TAG, contents);
316 }
317
318}