/* Sets the default subscription. If only one phone instance is active that
   * subscription is set as default subscription. If both phone instances
   * are active the first instance "0" is set as default subscription
   */
  public static void setDefaultSubscription(int subId) {
    SystemProperties.set(PROPERTY_DEFAULT_SUBSCRIPTION, Integer.toString(subId));
    int phoneId = SubscriptionController.getInstance().getPhoneId(subId);

    synchronized (sLockProxyPhones) {
      // Set the default phone in base class
      if (phoneId >= 0 && phoneId < sProxyPhones.length) {
        sProxyPhone = sProxyPhones[phoneId];
        sCommandsInterface = sCommandsInterfaces[phoneId];
        sMadeDefaults = true;
      }
    }

    // Update MCC MNC device configuration information
    String defaultMccMnc = TelephonyManager.getDefault().getSimOperatorNumericForPhone(phoneId);
    if (DBG) Rlog.d(LOG_TAG, "update mccmnc=" + defaultMccMnc);
    MccTable.updateMccMncConfiguration(sContext, defaultMccMnc, false);

    // Broadcast an Intent for default sub change
    Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
    intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
    SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
    Rlog.d(
        LOG_TAG,
        "setDefaultSubscription : " + subId + " Broadcasting Default Subscription Changed...");
    sContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
  }
  /**
   * Gets a list of all calendars not subscribed to sorted by primary key.
   *
   * @return List of Calendar
   * @throws Exception
   */
  public Set getUnsubscribedCalendarList(String email, Session session) throws Exception {
    List result = null;

    Set subscriptions = SubscriptionController.getController().getSubscriberList(email, session);
    List subscriptionsList = new ArrayList();

    Iterator i = subscriptions.iterator();
    while (i.hasNext()) {
      Subscriber subscriber = (Subscriber) i.next();
      subscriptionsList.add(subscriber.getCalendar().getId());
    }

    Criteria criteria = session.createCriteria(Calendar.class);
    if (subscriptionsList.size() > 0)
      criteria.add(Expression.not(Expression.in("id", subscriptionsList.toArray())));
    criteria.addOrder(Order.asc("name"));

    Set set = new LinkedHashSet();
    set.addAll(criteria.list());

    return set;
  }
  protected ServiceStateTracker(PhoneBase phoneBase, CommandsInterface ci, CellInfo cellInfo) {
    mPhoneBase = phoneBase;
    mCellInfo = cellInfo;
    mCi = ci;
    mVoiceCapable =
        mPhoneBase
            .getContext()
            .getResources()
            .getBoolean(com.android.internal.R.bool.config_voice_capable);
    mUiccController = UiccController.getInstance();
    mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
    mCi.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
    mCi.registerForCellInfoList(this, EVENT_UNSOL_CELL_INFO_LIST, null);

    mSubscriptionController = SubscriptionController.getInstance();
    mSubscriptionManager = SubscriptionManager.from(phoneBase.getContext());
    mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);

    mPhoneBase.setSystemProperty(
        TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
        ServiceState.rilRadioTechnologyToString(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
    mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
  }
  /** FIXME replace this with some other way of making these instances */
  public static void makeDefaultPhone(Context context) {
    synchronized (sLockProxyPhones) {
      if (!sMadeDefaults) {
        sContext = context;

        // create the telephony device controller.
        TelephonyDevController.create();

        int retryCount = 0;
        for (; ; ) {
          boolean hasException = false;
          retryCount++;

          try {
            // use UNIX domain socket to
            // prevent subsequent initialization
            new LocalServerSocket("com.android.internal.telephony");
          } catch (java.io.IOException ex) {
            hasException = true;
          }

          if (!hasException) {
            break;
          } else if (retryCount > SOCKET_OPEN_MAX_RETRY) {
            throw new RuntimeException("PhoneFactory probably already running");
          } else {
            try {
              Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
            } catch (InterruptedException er) {
            }
          }
        }

        sPhoneNotifier = new DefaultPhoneNotifier();

        int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
        Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);

        /* In case of multi SIM mode two instances of PhoneProxy, RIL are created,
        where as in single SIM mode only instance. isMultiSimEnabled() function checks
        whether it is single SIM or multi SIM mode */
        int numPhones = TelephonyManager.getDefault().getPhoneCount();
        int[] networkModes = new int[numPhones];
        sProxyPhones = new PhoneProxy[numPhones];
        sCommandsInterfaces = new RIL[numPhones];

        for (int i = 0; i < numPhones; i++) {
          // reads the system properties and makes commandsinterface
          // Get preferred network type.
          networkModes[i] = RILConstants.PREFERRED_NETWORK_MODE;

          Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i]));
          sCommandsInterfaces[i] = new RIL(context, networkModes[i], cdmaSubscription, i);
        }
        Rlog.i(LOG_TAG, "Creating SubscriptionController");
        SubscriptionController.init(context, sCommandsInterfaces);

        // Instantiate UiccController so that all other classes can just
        // call getInstance()
        mUiccController = UiccController.make(context, sCommandsInterfaces);

        for (int i = 0; i < numPhones; i++) {
          PhoneBase phone = null;
          int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
          if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
            phone = new GSMPhone(context, sCommandsInterfaces[i], sPhoneNotifier, i);
          } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
            phone = new CDMALTEPhone(context, sCommandsInterfaces[i], sPhoneNotifier, i);
          }
          Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);

          sProxyPhones[i] = new PhoneProxy(phone);
        }
        mProxyController =
            ProxyController.getInstance(
                context, sProxyPhones, mUiccController, sCommandsInterfaces);

        // Set the default phone in base class.
        // FIXME: This is a first best guess at what the defaults will be. It
        // FIXME: needs to be done in a more controlled manner in the future.
        sProxyPhone = sProxyPhones[0];
        sCommandsInterface = sCommandsInterfaces[0];

        // Ensure that we have a default SMS app. Requesting the app with
        // updateIfNeeded set to true is enough to configure a default SMS app.
        ComponentName componentName =
            SmsApplication.getDefaultSmsApplication(context, true /* updateIfNeeded */);
        String packageName = "NONE";
        if (componentName != null) {
          packageName = componentName.getPackageName();
        }
        Rlog.i(LOG_TAG, "defaultSmsApplication: " + packageName);

        // Set up monitor to watch for changes to SMS packages
        SmsApplication.initSmsPackageMonitor(context);

        sMadeDefaults = true;

        Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
        sSubInfoRecordUpdater =
            new SubscriptionInfoUpdater(context, sProxyPhones, sCommandsInterfaces);
        SubscriptionController.getInstance().updatePhonesAvailability(sProxyPhones);

        // Start monitoring after defaults have been made.
        // Default phone must be ready before ImsPhone is created
        // because ImsService might need it when it is being opened.
        for (int i = 0; i < numPhones; i++) {
          sProxyPhones[i].startMonitoringImsService();
        }
      }
    }
  }
  public static void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    pw.println("PhoneFactory:");
    PhoneProxy[] phones = (PhoneProxy[]) PhoneFactory.getPhones();
    int i = -1;
    for (PhoneProxy phoneProxy : phones) {
      PhoneBase phoneBase;
      i += 1;

      try {
        phoneBase = (PhoneBase) phoneProxy.getActivePhone();
        phoneBase.dump(fd, pw, args);
      } catch (Exception e) {
        pw.println("Telephony DebugService: Could not get Phone[" + i + "] e=" + e);
        continue;
      }

      pw.flush();
      pw.println("++++++++++++++++++++++++++++++++");

      try {
        ((IccCardProxy) phoneProxy.getIccCard()).dump(fd, pw, args);
      } catch (Exception e) {
        e.printStackTrace();
      }
      pw.flush();
      pw.println("++++++++++++++++++++++++++++++++");
    }

    try {
      DctController.getInstance().dump(fd, pw, args);
    } catch (Exception e) {
      e.printStackTrace();
    }

    try {
      mUiccController.dump(fd, pw, args);
    } catch (Exception e) {
      e.printStackTrace();
    }
    pw.flush();
    pw.println("++++++++++++++++++++++++++++++++");

    try {
      SubscriptionController.getInstance().dump(fd, pw, args);
    } catch (Exception e) {
      e.printStackTrace();
    }
    pw.flush();
    pw.println("++++++++++++++++++++++++++++++++");

    try {
      sSubInfoRecordUpdater.dump(fd, pw, args);
    } catch (Exception e) {
      e.printStackTrace();
    }
    pw.flush();

    pw.println("++++++++++++++++++++++++++++++++");
    synchronized (sLocalLogs) {
      final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
      for (String key : sLocalLogs.keySet()) {
        ipw.println(key);
        ipw.increaseIndent();
        sLocalLogs.get(key).dump(fd, ipw, args);
        ipw.decreaseIndent();
      }
      ipw.flush();
    }
  }
 /* Gets the default subscription */
 public static int getDefaultSubscription() {
   return SubscriptionController.getInstance().getDefaultSubId();
 }
  /**
   * TODO: Simplify more, as no one is interested in what happened only what the current list
   * contains.
   */
  private synchronized void updateSubscriptionInfoByIccId() {
    logd("updateSubscriptionInfoByIccId:+ Start");

    mSubscriptionManager.clearSubscriptionInfo();

    for (int i = 0; i < PROJECT_SIM_NUM; i++) {
      mInsertSimState[i] = SIM_NOT_CHANGE;
    }

    int insertedSimCount = PROJECT_SIM_NUM;
    for (int i = 0; i < PROJECT_SIM_NUM; i++) {
      if (ICCID_STRING_FOR_NO_SIM.equals(mIccId[i])) {
        insertedSimCount--;
        mInsertSimState[i] = SIM_NOT_INSERT;
      }
    }
    logd("insertedSimCount = " + insertedSimCount);

    int index = 0;
    for (int i = 0; i < PROJECT_SIM_NUM; i++) {
      if (mInsertSimState[i] == SIM_NOT_INSERT) {
        continue;
      }
      index = 2;
      for (int j = i + 1; j < PROJECT_SIM_NUM; j++) {
        if (mInsertSimState[j] == SIM_NOT_CHANGE && mIccId[i].equals(mIccId[j])) {
          mInsertSimState[i] = 1;
          mInsertSimState[j] = index;
          index++;
        }
      }
    }

    ContentResolver contentResolver = mContext.getContentResolver();
    String[] oldIccId = new String[PROJECT_SIM_NUM];
    for (int i = 0; i < PROJECT_SIM_NUM; i++) {
      oldIccId[i] = null;
      List<SubscriptionInfo> oldSubInfo =
          SubscriptionController.getInstance()
              .getSubInfoUsingSlotIdWithCheck(i, false, mContext.getOpPackageName());
      if (oldSubInfo != null) {
        oldIccId[i] = oldSubInfo.get(0).getIccId();
        logd("updateSubscriptionInfoByIccId: oldSubId = " + oldSubInfo.get(0).getSubscriptionId());
        if (mInsertSimState[i] == SIM_NOT_CHANGE && !mIccId[i].equals(oldIccId[i])) {
          mInsertSimState[i] = SIM_CHANGED;
        }
        if (mInsertSimState[i] != SIM_NOT_CHANGE) {
          ContentValues value = new ContentValues(1);
          value.put(SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
          contentResolver.update(
              SubscriptionManager.CONTENT_URI,
              value,
              SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
                  + "="
                  + Integer.toString(oldSubInfo.get(0).getSubscriptionId()),
              null);
        }
      } else {
        if (mInsertSimState[i] == SIM_NOT_CHANGE) {
          // no SIM inserted last time, but there is one SIM inserted now
          mInsertSimState[i] = SIM_CHANGED;
        }
        oldIccId[i] = ICCID_STRING_FOR_NO_SIM;
        logd("updateSubscriptionInfoByIccId: No SIM in slot " + i + " last time");
      }
    }

    for (int i = 0; i < PROJECT_SIM_NUM; i++) {
      logd(
          "updateSubscriptionInfoByIccId: oldIccId["
              + i
              + "] = "
              + oldIccId[i]
              + ", sIccId["
              + i
              + "] = "
              + mIccId[i]);
    }

    // check if the inserted SIM is new SIM
    int nNewCardCount = 0;
    int nNewSimStatus = 0;
    for (int i = 0; i < PROJECT_SIM_NUM; i++) {
      if (mInsertSimState[i] == SIM_NOT_INSERT) {
        logd("updateSubscriptionInfoByIccId: No SIM inserted in slot " + i + " this time");
      } else {
        if (mInsertSimState[i] > 0) {
          // some special SIMs may have the same IccIds, add suffix to distinguish them
          // FIXME: addSubInfoRecord can return an error.
          mSubscriptionManager.addSubscriptionInfoRecord(
              mIccId[i] + Integer.toString(mInsertSimState[i]), i);
          logd("SUB" + (i + 1) + " has invalid IccId");
        } else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ {
          mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i], i);
        }
        if (isNewSim(mIccId[i], oldIccId)) {
          nNewCardCount++;
          switch (i) {
            case PhoneConstants.SUB1:
              nNewSimStatus |= STATUS_SIM1_INSERTED;
              break;
            case PhoneConstants.SUB2:
              nNewSimStatus |= STATUS_SIM2_INSERTED;
              break;
            case PhoneConstants.SUB3:
              nNewSimStatus |= STATUS_SIM3_INSERTED;
              break;
              // case PhoneConstants.SUB3:
              //    nNewSimStatus |= STATUS_SIM4_INSERTED;
              //    break;
          }

          mInsertSimState[i] = SIM_NEW;
        }
      }
    }

    for (int i = 0; i < PROJECT_SIM_NUM; i++) {
      if (mInsertSimState[i] == SIM_CHANGED) {
        mInsertSimState[i] = SIM_REPOSITION;
      }
      logd("updateSubscriptionInfoByIccId: sInsertSimState[" + i + "] = " + mInsertSimState[i]);
    }

    List<SubscriptionInfo> subInfos = mSubscriptionManager.getActiveSubscriptionInfoList();
    int nSubCount = (subInfos == null) ? 0 : subInfos.size();
    logd("updateSubscriptionInfoByIccId: nSubCount = " + nSubCount);
    for (int i = 0; i < nSubCount; i++) {
      SubscriptionInfo temp = subInfos.get(i);

      String msisdn =
          TelephonyManager.getDefault().getLine1NumberForSubscriber(temp.getSubscriptionId());

      if (msisdn != null) {
        ContentValues value = new ContentValues(1);
        value.put(SubscriptionManager.NUMBER, msisdn);
        contentResolver.update(
            SubscriptionManager.CONTENT_URI,
            value,
            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
                + "="
                + Integer.toString(temp.getSubscriptionId()),
            null);
      }
    }

    // Ensure the modems are mapped correctly
    mSubscriptionManager.setDefaultDataSubId(mSubscriptionManager.getDefaultDataSubId());

    SubscriptionController.getInstance().notifySubscriptionInfoChanged();
    logd("updateSubscriptionInfoByIccId:- SsubscriptionInfo update complete");
  }
  private void handleSimLoaded(int slotId) {
    logd("handleSimStateLoadedInternal: slotId: " + slotId);

    // The SIM should be loaded at this state, but it is possible in cases such as SIM being
    // removed or a refresh RESET that the IccRecords could be null. The right behavior is to
    // not broadcast the SIM loaded.
    IccRecords records = mPhone[slotId].getIccCard().getIccRecords();
    if (records == null) { // Possibly a race condition.
      logd("onRecieve: IccRecords null");
      return;
    }
    if (records.getIccId() == null) {
      logd("onRecieve: IccID null");
      return;
    }
    mIccId[slotId] = records.getIccId();

    if (isAllIccIdQueryDone()) {
      updateSubscriptionInfoByIccId();
    }

    int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
    int[] subIds = SubscriptionController.getInstance().getSubId(slotId);
    if (subIds != null) { // Why an array?
      subId = subIds[0];
    }

    if (SubscriptionManager.isValidSubscriptionId(subId)) {
      String operator = records.getOperatorNumeric();
      if (operator != null) {
        if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
          MccTable.updateMccMncConfiguration(mContext, operator, false);
        }
        SubscriptionController.getInstance().setMccMnc(operator, subId);
      } else {
        logd("EVENT_RECORDS_LOADED Operator name is null");
      }
      TelephonyManager tm = TelephonyManager.getDefault();
      String msisdn = tm.getLine1NumberForSubscriber(subId);
      ContentResolver contentResolver = mContext.getContentResolver();

      if (msisdn != null) {
        ContentValues number = new ContentValues(1);
        number.put(SubscriptionManager.NUMBER, msisdn);
        contentResolver.update(
            SubscriptionManager.CONTENT_URI,
            number,
            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId),
            null);
      }

      SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
      String nameToSet;
      String simCarrierName = tm.getSimOperatorNameForSubscription(subId);
      ContentValues name = new ContentValues(1);

      if (subInfo != null
          && subInfo.getNameSource() != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
        if (!TextUtils.isEmpty(simCarrierName)) {
          nameToSet = simCarrierName;
        } else {
          nameToSet = "CARD " + Integer.toString(slotId + 1);
        }
        name.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
        logd("sim name = " + nameToSet);
        contentResolver.update(
            SubscriptionManager.CONTENT_URI,
            name,
            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId),
            null);
      }

      /* Update preferred network type and network selection mode on SIM change.
       * Storing last subId in SharedPreference for now to detect SIM change. */
      SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
      int storedSubId = sp.getInt(CURR_SUBID + slotId, -1);

      if (storedSubId != subId) {
        int networkType = RILConstants.PREFERRED_NETWORK_MODE;

        // Set the modem network mode
        mPhone[slotId].setPreferredNetworkType(networkType, null);
        Settings.Global.putInt(
            mPhone[slotId].getContext().getContentResolver(),
            Settings.Global.PREFERRED_NETWORK_MODE + subId,
            networkType);

        // Only support automatic selection mode on SIM change.
        mPhone[slotId].getNetworkSelectionMode(
            obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE, new Integer(slotId)));

        // Update stored subId
        SharedPreferences.Editor editor = sp.edit();
        editor.putInt(CURR_SUBID + slotId, subId);
        editor.apply();
      }
    } else {
      logd("Invalid subId, could not update ContentResolver");
    }

    // Update set of enabled carrier apps now that the privilege rules may have changed.
    CarrierAppUtils.disableCarrierAppsUntilPrivileged(
        mContext.getOpPackageName(),
        mPackageManager,
        TelephonyManager.getDefault(),
        mCurrentlyActiveUserId);

    broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
    updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
  }