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(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(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(imsFeature, tech, capability, subIdBundle, 150 newValue); 151 saveSubIdBundleToXml(subId, subIdBundle); 152 } 153 return true; 154 } 155 getProvisioningStatusFromSubIdBundle(int imsFeature, int tech, int capability, PersistableBundle subIdBundle)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 setProvisioningStatusToSubIdBundle(int imsFeature, int tech, int capability, PersistableBundle subIdBundle, int newStatus)181 private void setProvisioningStatusToSubIdBundle(int imsFeature, int tech, 182 int capability, PersistableBundle subIdBundle, int newStatus) { 183 logd("set provisioning status " + newStatus + " ImsFeature " 184 + imsFeature + " tech " + tech + " capa " + capability); 185 186 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 getIntValueFromBundle(String key, PersistableBundle bundle)204 private int getIntValueFromBundle(String key, PersistableBundle bundle) { 205 int value = bundle.getInt(key, STATUS_NOT_SET); 206 logd("get value " + value); 207 return value; 208 } 209 210 // Return subIdBundle from imsprovisioningstatus_{subId}.xml readSubIdBundleFromXml(int subId)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 saveSubIdBundleToXml(int subId, PersistableBundle subIdBundle)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 getUTProvisioningStatus(int subId, int tech)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 */ getMmTelCapabilityProvisioningBitfield(int subId, int tech)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 getMmTelProvisioningKey(int subId, int tech)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 getFileName(int subId)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 clear()285 void clear() { 286 synchronized (mLock) { 287 mSubIdBundleArray.clear(); 288 } 289 } 290 291 @VisibleForTesting setProvisioningToXml(int subId, PersistableBundle subIdBundle, String[] infoArray)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 loge(String contents)303 private void loge(String contents) { 304 Log.e(LOG_TAG, contents); 305 } 306 logd(String contents)307 private void logd(String contents) { 308 Log.d(LOG_TAG, contents); 309 } 310 311 } 312