1 /* 2 * Copyright (C) 2018 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.internal.telephony; 18 19 import android.content.Context; 20 import android.os.AsyncResult; 21 import android.os.Handler; 22 import android.os.Message; 23 import android.os.PowerManager; 24 import android.os.SystemProperties; 25 import android.os.storage.StorageManager; 26 import android.telephony.PhoneCapability; 27 import android.telephony.Rlog; 28 import android.telephony.TelephonyManager; 29 import android.util.Log; 30 31 import java.util.HashMap; 32 import java.util.Map; 33 import java.util.NoSuchElementException; 34 35 /** 36 * This class manages phone's configuration which defines the potential capability (static) of the 37 * phone and its current activated capability (current). 38 * It gets and monitors static and current phone capability from the modem; send broadcast 39 * if they change, and and sends commands to modem to enable or disable phones. 40 */ 41 public class PhoneConfigurationManager { 42 public static final String DSDA = "dsda"; 43 public static final String DSDS = "dsds"; 44 public static final String TSTS = "tsts"; 45 public static final String SSSS = ""; 46 private static final String LOG_TAG = "PhoneCfgMgr"; 47 private static final int EVENT_SWITCH_DSDS_CONFIG_DONE = 100; 48 private static final int EVENT_GET_MODEM_STATUS = 101; 49 private static final int EVENT_GET_MODEM_STATUS_DONE = 102; 50 private static final int EVENT_GET_PHONE_CAPABILITY_DONE = 103; 51 52 private static PhoneConfigurationManager sInstance = null; 53 private final Context mContext; 54 private PhoneCapability mStaticCapability; 55 private final RadioConfig mRadioConfig; 56 private final MainThreadHandler mHandler; 57 private final Phone[] mPhones; 58 private final Map<Integer, Boolean> mPhoneStatusMap; 59 60 /** 61 * Init method to instantiate the object 62 * Should only be called once. 63 */ init(Context context)64 public static PhoneConfigurationManager init(Context context) { 65 synchronized (PhoneConfigurationManager.class) { 66 if (sInstance == null) { 67 sInstance = new PhoneConfigurationManager(context); 68 } else { 69 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 70 } 71 return sInstance; 72 } 73 } 74 75 /** 76 * Constructor. 77 * @param context context needed to send broadcast. 78 */ PhoneConfigurationManager(Context context)79 private PhoneConfigurationManager(Context context) { 80 mContext = context; 81 // TODO: send commands to modem once interface is ready. 82 TelephonyManager telephonyManager = new TelephonyManager(context); 83 //initialize with default, it'll get updated when RADIO is ON/AVAILABLE 84 mStaticCapability = getDefaultCapability(); 85 mRadioConfig = RadioConfig.getInstance(mContext); 86 mHandler = new MainThreadHandler(); 87 mPhoneStatusMap = new HashMap<>(); 88 89 notifyCapabilityChanged(); 90 91 mPhones = PhoneFactory.getPhones(); 92 if (!StorageManager.inCryptKeeperBounce()) { 93 for (Phone phone : mPhones) { 94 phone.mCi.registerForAvailable(mHandler, Phone.EVENT_RADIO_AVAILABLE, phone); 95 } 96 } else { 97 for (Phone phone : mPhones) { 98 phone.mCi.registerForOn(mHandler, Phone.EVENT_RADIO_ON, phone); 99 } 100 } 101 } 102 getDefaultCapability()103 private PhoneCapability getDefaultCapability() { 104 if (getPhoneCount() > 1) { 105 return PhoneCapability.DEFAULT_DSDS_CAPABILITY; 106 } else { 107 return PhoneCapability.DEFAULT_SSSS_CAPABILITY; 108 } 109 } 110 111 /** 112 * Static method to get instance. 113 */ getInstance()114 public static PhoneConfigurationManager getInstance() { 115 if (sInstance == null) { 116 Log.wtf(LOG_TAG, "getInstance null"); 117 } 118 119 return sInstance; 120 } 121 122 /** 123 * Handler class to handle callbacks 124 */ 125 private final class MainThreadHandler extends Handler { 126 @Override handleMessage(Message msg)127 public void handleMessage(Message msg) { 128 AsyncResult ar; 129 Phone phone = null; 130 switch (msg.what) { 131 case Phone.EVENT_RADIO_AVAILABLE: 132 case Phone.EVENT_RADIO_ON: 133 log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON"); 134 ar = (AsyncResult) msg.obj; 135 if (ar.userObj != null && ar.userObj instanceof Phone) { 136 phone = (Phone) ar.userObj; 137 updatePhoneStatus(phone); 138 } else { 139 // phone is null 140 log("Unable to add phoneStatus to cache. " 141 + "No phone object provided for event " + msg.what); 142 } 143 getStaticPhoneCapability(); 144 break; 145 case EVENT_SWITCH_DSDS_CONFIG_DONE: 146 ar = (AsyncResult) msg.obj; 147 if (ar != null && ar.exception == null) { 148 int numOfLiveModems = msg.arg1; 149 setMultiSimProperties(numOfLiveModems); 150 } else { 151 log(msg.what + " failure. Not switching multi-sim config." + ar.exception); 152 } 153 break; 154 case EVENT_GET_MODEM_STATUS_DONE: 155 ar = (AsyncResult) msg.obj; 156 if (ar != null && ar.exception == null) { 157 int phoneId = msg.arg1; 158 boolean enabled = (boolean) ar.result; 159 //update the cache each time getModemStatus is requested 160 addToPhoneStatusCache(phoneId, enabled); 161 } else { 162 log(msg.what + " failure. Not updating modem status." + ar.exception); 163 } 164 break; 165 case EVENT_GET_PHONE_CAPABILITY_DONE: 166 ar = (AsyncResult) msg.obj; 167 if (ar != null && ar.exception == null) { 168 mStaticCapability = (PhoneCapability) ar.result; 169 notifyCapabilityChanged(); 170 } else { 171 log(msg.what + " failure. Not getting phone capability." + ar.exception); 172 } 173 } 174 } 175 } 176 177 /** 178 * Enable or disable phone 179 * 180 * @param phone which phone to operate on 181 * @param enable true or false 182 * @param result the message to sent back when it's done. 183 */ enablePhone(Phone phone, boolean enable, Message result)184 public void enablePhone(Phone phone, boolean enable, Message result) { 185 if (phone == null) { 186 log("enablePhone failed phone is null"); 187 return; 188 } 189 phone.mCi.enableModem(enable, result); 190 } 191 192 /** 193 * Get phone status (enabled/disabled) 194 * first query cache, if the status is not in cache, 195 * add it to cache and return a default value true (non-blocking). 196 * 197 * @param phone which phone to operate on 198 */ getPhoneStatus(Phone phone)199 public boolean getPhoneStatus(Phone phone) { 200 if (phone == null) { 201 log("getPhoneStatus failed phone is null"); 202 return false; 203 } 204 205 int phoneId = phone.getPhoneId(); 206 207 //use cache if the status has already been updated/queried 208 try { 209 return getPhoneStatusFromCache(phoneId); 210 } catch (NoSuchElementException ex) { 211 updatePhoneStatus(phone); 212 // Return true if modem status cannot be retrieved. For most cases, modem status 213 // is on. And for older version modems, GET_MODEM_STATUS and disable modem are not 214 // supported. Modem is always on. 215 //TODO: this should be fixed in R to support a third status UNKNOWN b/131631629 216 return true; 217 } 218 } 219 220 /** 221 * Get phone status (enabled/disabled) directly from modem, and use a result Message object 222 * Note: the caller of this method is reponsible to call this in a blocking fashion as well 223 * as read the results and handle the error case. 224 * (In order to be consistent, in error case, we should return default value of true; refer 225 * to #getPhoneStatus method) 226 * 227 * @param phone which phone to operate on 228 * @param result message that will be updated with result 229 */ getPhoneStatusFromModem(Phone phone, Message result)230 public void getPhoneStatusFromModem(Phone phone, Message result) { 231 if (phone == null) { 232 log("getPhoneStatus failed phone is null"); 233 } 234 phone.mCi.getModemStatus(result); 235 } 236 237 /** 238 * return modem status from cache, NoSuchElementException if phoneId not in cache 239 * @param phoneId 240 */ getPhoneStatusFromCache(int phoneId)241 public boolean getPhoneStatusFromCache(int phoneId) throws NoSuchElementException { 242 if (mPhoneStatusMap.containsKey(phoneId)) { 243 return mPhoneStatusMap.get(phoneId); 244 } else { 245 throw new NoSuchElementException("phoneId not found: " + phoneId); 246 } 247 } 248 249 /** 250 * method to call RIL getModemStatus 251 */ updatePhoneStatus(Phone phone)252 private void updatePhoneStatus(Phone phone) { 253 Message result = Message.obtain( 254 mHandler, EVENT_GET_MODEM_STATUS_DONE, phone.getPhoneId(), 0 /**dummy arg*/); 255 phone.mCi.getModemStatus(result); 256 } 257 258 /** 259 * Add status of the phone to the status HashMap 260 * @param phoneId 261 * @param status 262 */ addToPhoneStatusCache(int phoneId, boolean status)263 public void addToPhoneStatusCache(int phoneId, boolean status) { 264 mPhoneStatusMap.put(phoneId, status); 265 } 266 267 /** 268 * Returns how many phone objects the device supports. 269 */ getPhoneCount()270 public int getPhoneCount() { 271 TelephonyManager tm = new TelephonyManager(mContext); 272 return tm.getPhoneCount(); 273 } 274 275 /** 276 * get static overall phone capabilities for all phones. 277 */ getStaticPhoneCapability()278 public synchronized PhoneCapability getStaticPhoneCapability() { 279 if (getDefaultCapability().equals(mStaticCapability)) { 280 log("getStaticPhoneCapability: sending the request for getting PhoneCapability"); 281 Message callback = Message.obtain( 282 mHandler, EVENT_GET_PHONE_CAPABILITY_DONE); 283 mRadioConfig.getPhoneCapability(callback); 284 } 285 return mStaticCapability; 286 } 287 288 /** 289 * get configuration related status of each phone. 290 */ getCurrentPhoneCapability()291 public PhoneCapability getCurrentPhoneCapability() { 292 return getStaticPhoneCapability(); 293 } 294 getNumberOfModemsWithSimultaneousDataConnections()295 public int getNumberOfModemsWithSimultaneousDataConnections() { 296 return mStaticCapability.maxActiveData; 297 } 298 notifyCapabilityChanged()299 private void notifyCapabilityChanged() { 300 PhoneNotifier notifier = new DefaultPhoneNotifier(); 301 302 notifier.notifyPhoneCapabilityChanged(mStaticCapability); 303 } 304 305 /** 306 * Switch configs to enable multi-sim or switch back to single-sim 307 * @param numOfSims number of active sims we want to switch to 308 */ switchMultiSimConfig(int numOfSims)309 public void switchMultiSimConfig(int numOfSims) { 310 log("switchMultiSimConfig: with numOfSims = " + numOfSims); 311 if (getStaticPhoneCapability().logicalModemList.size() < numOfSims) { 312 log("switchMultiSimConfig: Phone is not capable of enabling " 313 + numOfSims + " sims, exiting!"); 314 return; 315 } 316 if (getPhoneCount() != numOfSims) { 317 log("switchMultiSimConfig: sending the request for switching"); 318 Message callback = Message.obtain( 319 mHandler, EVENT_SWITCH_DSDS_CONFIG_DONE, numOfSims, 0 /**dummy arg*/); 320 mRadioConfig.setModemsConfig(numOfSims, callback); 321 } else { 322 log("switchMultiSimConfig: No need to switch. getNumOfActiveSims is already " 323 + numOfSims); 324 } 325 } 326 327 /** 328 * Get whether reboot is required or not after making changes to modem configurations. 329 * Return value defaults to true 330 */ isRebootRequiredForModemConfigChange()331 public boolean isRebootRequiredForModemConfigChange() { 332 String rebootRequired = SystemProperties.get( 333 TelephonyProperties.PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE); 334 log("isRebootRequiredForModemConfigChange: isRebootRequired = " + rebootRequired); 335 return !rebootRequired.equals("false"); 336 } 337 338 /** 339 * Helper method to set system properties for setting multi sim configs, 340 * as well as doing the phone reboot 341 * NOTE: In order to support more than 3 sims, we need to change this method. 342 * @param numOfSims number of active sims 343 */ setMultiSimProperties(int numOfSims)344 private void setMultiSimProperties(int numOfSims) { 345 String finalMultiSimConfig; 346 switch(numOfSims) { 347 case 3: 348 finalMultiSimConfig = TSTS; 349 break; 350 case 2: 351 finalMultiSimConfig = DSDS; 352 break; 353 default: 354 finalMultiSimConfig = SSSS; 355 } 356 357 SystemProperties.set(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG, finalMultiSimConfig); 358 if (isRebootRequiredForModemConfigChange()) { 359 log("setMultiSimProperties: Rebooting due to switching multi-sim config to " 360 + finalMultiSimConfig); 361 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 362 pm.reboot("Switching to " + finalMultiSimConfig); 363 } else { 364 log("setMultiSimProperties: Rebooting is not required to switch multi-sim config to " 365 + finalMultiSimConfig); 366 } 367 } 368 log(String s)369 private static void log(String s) { 370 Rlog.d(LOG_TAG, s); 371 } 372 } 373