• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.phone;
18 
19 import android.content.Context;
20 import android.content.SharedPreferences;
21 import android.os.PersistableBundle;
22 import android.preference.PreferenceManager;
23 import android.telephony.ims.ProvisioningManager;
24 import android.telephony.ims.feature.ImsFeature;
25 import android.telephony.ims.feature.MmTelFeature;
26 import android.telephony.ims.stub.ImsRegistrationImplBase;
27 import android.util.Log;
28 import android.util.SparseArray;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 
32 import java.io.File;
33 import java.io.FileInputStream;
34 import java.io.FileNotFoundException;
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 
38 /**
39  * Provides a function to set/get Ims feature provisioning status in storage.
40  */
41 public 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 
ImsProvisioningLoader(Context context)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      */
getProvisioningStatus(int subId, @ImsFeature.FeatureType int imsFeature, int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech)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      */
setProvisioningStatus(int subId, @ImsFeature.FeatureType int imsFeature, int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned)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 
isFileExist(int subId)89     private boolean isFileExist(int subId) {
90         File file = new File(mContext.getFilesDir(), getFileName(subId));
91         return file.exists();
92     }
93 
initCache(int subId)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                     logd("check UT provisioning status " + UtProvisioningStatus);
114 
115                     if (STATUS_PROVISIONED == UtProvisioningStatus) {
116                         setProvisioningStatusToSubIdBundle(subId, 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 
getImsProvisioningStatus(int subId, int imsFeature, int tech, int capability)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(subId, imsFeature, tech,
134                 capability, subIdBundle);
135     }
136 
setImsFeatureProvisioning(int subId, int imsFeature, int tech, int capability, boolean isProvisioned)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(subId, imsFeature, tech, capability, subIdBundle,
150                     newValue);
151             saveSubIdBundleToXml(subId, subIdBundle);
152         }
153         return true;
154     }
155 
getProvisioningStatusFromSubIdBundle(int subId, int imsFeature, int tech, int capability, PersistableBundle subIdBundle)156     private int getProvisioningStatusFromSubIdBundle(int subId, 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("getProvisioningStatusFromSubIdBundle", subId, "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("getProvisioningStatusFromSubIdBundle", subId,
168                     "ImsFeature " + imsFeature + " does not exist in xml");
169             return STATUS_NOT_SET;
170         }
171 
172         PersistableBundle capabilityBundle = regTechBundle.getPersistableBundle(
173                 String.valueOf(tech));
174         if (capabilityBundle == null) {
175             logd("getProvisioningStatusFromSubIdBundle", subId,
176                     "RegistrationTech " + tech + " does not exist in xml");
177             return STATUS_NOT_SET;
178         }
179 
180         return getIntValueFromBundle(subId, tech, String.valueOf(capability), capabilityBundle);
181     }
182 
setProvisioningStatusToSubIdBundle(int subId, int imsFeature, int tech, int capability, PersistableBundle subIdBundle, int newStatus)183     private void setProvisioningStatusToSubIdBundle(int subId, int imsFeature, int tech,
184             int capability, PersistableBundle subIdBundle, int newStatus) {
185         logd("setProvisioningStatusToSubIdBundle", subId, "set provisioning status " + newStatus
186                 + " ImsFeature " + imsFeature + " tech " + tech + " capa " + capability);
187 
188         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
getIntValueFromBundle(int subId, int tech, String key, PersistableBundle bundle)206     private int getIntValueFromBundle(int subId, int tech, String key, PersistableBundle bundle) {
207         int value = bundle.getInt(key, STATUS_NOT_SET);
208         logd("getIntValueFromBundle", subId,
209                 "Cache hit, tech=" + tech + " capability=" + key + ": returning " + value);
210         return value;
211     }
212 
213     // Return subIdBundle from imsprovisioningstatus_{subId}.xml
readSubIdBundleFromXml(int subId)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 
saveSubIdBundleToXml(int subId, PersistableBundle subIdBundle)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 
getUTProvisioningStatus(int subId, int tech)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      */
getMmTelCapabilityProvisioningBitfield(int subId, int tech)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 
getMmTelProvisioningKey(int subId, int tech)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 
getFileName(int subId)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
clear()288     void clear() {
289         synchronized (mLock) {
290             mSubIdBundleArray.clear();
291         }
292     }
293 
294     @VisibleForTesting
setProvisioningToXml(int subId, PersistableBundle subIdBundle, String[] infoArray)295     void setProvisioningToXml(int subId, PersistableBundle subIdBundle,
296             String[] infoArray) {
297         for (String info : infoArray) {
298             String[] paramArray = info.split(",");
299             setProvisioningStatusToSubIdBundle(subId, Integer.valueOf(paramArray[0]),
300                     Integer.valueOf(paramArray[1]), Integer.valueOf(paramArray[2]),
301                     subIdBundle, Integer.valueOf(paramArray[3]));
302         }
303         saveSubIdBundleToXml(subId, subIdBundle);
304     }
305 
loge(String contents)306     private void loge(String contents) {
307         Log.e(LOG_TAG, contents);
308     }
309 
logd(String prefix, int subId, String contents)310     private void logd(String prefix, int subId, String contents) {
311         Log.d(LOG_TAG, prefix + "[" + subId + "]: " + contents);
312     }
313 
logd(String contents)314     private void logd(String contents) {
315         Log.d(LOG_TAG, contents);
316     }
317 
318 }
319