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