blob: 1238b9a9848787f1bed4f6c75eed493c15388616 [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) {
116 setProvisioningStatusToSubIdBundle(ImsFeature.FEATURE_MMTEL, tech,
117 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
133 return getProvisioningStatusFromSubIdBundle(imsFeature, tech,
134 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);
149 setProvisioningStatusToSubIdBundle(imsFeature, tech, capability, subIdBundle,
150 newValue);
151 saveSubIdBundleToXml(subId, subIdBundle);
152 }
153 return true;
154 }
155
156 private int getProvisioningStatusFromSubIdBundle(int imsFeature, int tech,
157 int capability, PersistableBundle subIdBundle) {
158 // If it doesn't exist in xml, return STATUS_NOT_SET
159 if (subIdBundle == null || subIdBundle.isEmpty()) {
160 logd("xml is empty");
161 return STATUS_NOT_SET;
162 }
163
164 PersistableBundle regTechBundle = subIdBundle.getPersistableBundle(
165 String.valueOf(imsFeature));
166 if (regTechBundle == null) {
167 logd("ImsFeature " + imsFeature + " is not exist in xml");
168 return STATUS_NOT_SET;
169 }
170
171 PersistableBundle capabilityBundle = regTechBundle.getPersistableBundle(
172 String.valueOf(tech));
173 if (capabilityBundle == null) {
174 logd("RegistrationTech " + tech + " is not exist in xml");
175 return STATUS_NOT_SET;
176 }
177
178 return getIntValueFromBundle(String.valueOf(capability), capabilityBundle);
179 }
180
181 private void setProvisioningStatusToSubIdBundle(int imsFeature, int tech,
182 int capability, PersistableBundle subIdBundle, int newStatus) {
joonhunshinf45e45b2022-02-03 06:37:55 +0000183 logd("set provisioning status " + newStatus + " ImsFeature "
184 + imsFeature + " tech " + tech + " capa " + capability);
185
hyosun873e6dc2021-12-22 14:56:03 +0000186 PersistableBundle regTechBundle = subIdBundle.getPersistableBundle(
187 String.valueOf(imsFeature));
188 if (regTechBundle == null) {
189 regTechBundle = new PersistableBundle();
190 subIdBundle.putPersistableBundle(String.valueOf(imsFeature), regTechBundle);
191 }
192
193 PersistableBundle capabilityBundle = regTechBundle.getPersistableBundle(
194 String.valueOf(tech));
195 if (capabilityBundle == null) {
196 capabilityBundle = new PersistableBundle();
197 regTechBundle.putPersistableBundle(String.valueOf(tech), capabilityBundle);
198 }
199
200 capabilityBundle.putInt(String.valueOf(capability), newStatus);
201 }
202
203 // Default value is STATUS_NOT_SET
204 private int getIntValueFromBundle(String key, PersistableBundle bundle) {
joonhunshinf45e45b2022-02-03 06:37:55 +0000205 int value = bundle.getInt(key, STATUS_NOT_SET);
206 logd("get value " + value);
207 return value;
hyosun873e6dc2021-12-22 14:56:03 +0000208 }
209
210 // Return subIdBundle from imsprovisioningstatus_{subId}.xml
211 private PersistableBundle readSubIdBundleFromXml(int subId) {
212 String fileName = getFileName(subId);
213
214 PersistableBundle subIdBundles = new PersistableBundle();
215 File file = null;
216 FileInputStream inFile = null;
217 synchronized (mLock) {
218 try {
219 file = new File(mContext.getFilesDir(), fileName);
220 inFile = new FileInputStream(file);
221 subIdBundles = PersistableBundle.readFromStream(inFile);
222 inFile.close();
223 } catch (FileNotFoundException e) {
224 logd(e.toString());
225 } catch (IOException e) {
226 loge(e.toString());
227 } catch (RuntimeException e) {
228 loge(e.toString());
229 }
230 }
231
232 return subIdBundles;
233 }
234
235 private void saveSubIdBundleToXml(int subId, PersistableBundle subIdBundle) {
236 String fileName = getFileName(subId);
237
238 if (subIdBundle == null || subIdBundle.isEmpty()) {
239 logd("subIdBundle is empty");
240 return;
241 }
242
243 FileOutputStream outFile = null;
244 synchronized (mLock) {
245 try {
246 outFile = new FileOutputStream(new File(mContext.getFilesDir(), fileName));
247 subIdBundle.writeToStream(outFile);
248 outFile.flush();
249 outFile.close();
250 } catch (IOException e) {
251 loge(e.toString());
252 } catch (RuntimeException e) {
253 loge(e.toString());
254 }
255 }
256 }
257
258 private int getUTProvisioningStatus(int subId, int tech) {
259 return getMmTelCapabilityProvisioningBitfield(subId, tech) > 0 ? STATUS_PROVISIONED
260 : STATUS_NOT_SET;
261 }
262
263 /**
264 * @return the bitfield containing the MmTel provisioning for the provided subscription and
265 * technology. The bitfield should mirror the bitfield defined by
266 * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
267 */
268 private int getMmTelCapabilityProvisioningBitfield(int subId, int tech) {
269 String key = getMmTelProvisioningKey(subId, tech);
270 // Default is no capabilities are provisioned.
271 return mTelephonySharedPreferences.getInt(key, 0 /*default*/);
272 }
273
274 private String getMmTelProvisioningKey(int subId, int tech) {
275 // Resulting key is provision_ims_mmtel_{subId}_{tech}
276 return PREF_PROVISION_IMS_MMTEL_PREFIX + subId + "_" + tech;
277 }
278
279 private String getFileName(int subId) {
280 // Resulting name is imsprovisioningstatus_{subId}.xml
281 return PROVISIONING_FILE_NAME_PREF + subId + ".xml";
282 }
283
284 @VisibleForTesting
285 void clear() {
286 synchronized (mLock) {
287 mSubIdBundleArray.clear();
288 }
289 }
290
291 @VisibleForTesting
292 void setProvisioningToXml(int subId, PersistableBundle subIdBundle,
293 String[] infoArray) {
294 for (String info : infoArray) {
295 String[] paramArray = info.split(",");
296 setProvisioningStatusToSubIdBundle(Integer.valueOf(paramArray[0]),
297 Integer.valueOf(paramArray[1]), Integer.valueOf(paramArray[2]),
298 subIdBundle, Integer.valueOf(paramArray[3]));
299 }
300 saveSubIdBundleToXml(subId, subIdBundle);
301 }
302
303 private void loge(String contents) {
304 Log.e(LOG_TAG, contents);
305 }
306
307 private void logd(String contents) {
308 Log.d(LOG_TAG, contents);
309 }
310
311}