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