Merge "Display "No service" on status bar when SIM unready"
diff --git a/src/java/com/android/internal/telephony/cat/AppInterface.java b/src/java/com/android/internal/telephony/cat/AppInterface.java
old mode 100644
new mode 100755
index c78b7f8..1f2d3a0
--- a/src/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/src/java/com/android/internal/telephony/cat/AppInterface.java
@@ -84,6 +84,7 @@
         SET_UP_MENU(0x25),
         SET_UP_CALL(0x10),
         PROVIDE_LOCAL_INFORMATION(0x26),
+        LANGUAGE_NOTIFICATION(0x35),
         OPEN_CHANNEL(0x40),
         CLOSE_CHANNEL(0x41),
         RECEIVE_DATA(0x42),
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
old mode 100644
new mode 100755
index 7e70212..cd7a756
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -16,15 +16,21 @@
 
 package com.android.internal.telephony.cat;
 
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.backup.BackupManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources.NotFoundException;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.LocaleList;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -450,6 +456,18 @@
                     ((CallSetupParams) cmdParams).mConfirmMsg.text = message.toString();
                 }
                 break;
+            case LANGUAGE_NOTIFICATION:
+                String language = ((LanguageParams) cmdParams).mLanguage;
+                ResultCode result = ResultCode.OK;
+                if (language != null && language.length() > 0) {
+                    try {
+                        changeLanguage(language);
+                    } catch (RemoteException e) {
+                        result = ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS;
+                    }
+                }
+                sendTerminalResponse(cmdParams.mCmdDet, result, false, 0, null);
+                return;
             case OPEN_CHANNEL:
             case CLOSE_CHANNEL:
             case RECEIVE_DATA:
@@ -1129,4 +1147,13 @@
             mCmdIf.reportStkServiceIsRunning(null);
         }
     }
+
+    private void changeLanguage(String language) throws RemoteException {
+        IActivityManager am = ActivityManagerNative.getDefault();
+        Configuration config = am.getConfiguration();
+        config.setLocales(new LocaleList(new Locale(language), LocaleList.getDefault()));
+        config.userSetLocale = true;
+        am.updatePersistentConfiguration(config);
+        BackupManager.dataChanged("com.android.providers.settings");
+    }
 }
diff --git a/src/java/com/android/internal/telephony/cat/CommandParams.java b/src/java/com/android/internal/telephony/cat/CommandParams.java
old mode 100644
new mode 100755
index 7dfedab..59cd414
--- a/src/java/com/android/internal/telephony/cat/CommandParams.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParams.java
@@ -150,6 +150,15 @@
     }
 }
 
+class LanguageParams extends CommandParams {
+    String mLanguage;
+
+    LanguageParams(CommandDetails cmdDet, String lang) {
+        super(cmdDet);
+        mLanguage = lang;
+    }
+}
+
 class SelectItemParams extends CommandParams {
     Menu mMenu = null;
     boolean mLoadTitleIcon = false;
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 3dd5337..eb92888 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -19,12 +19,15 @@
 import android.graphics.Bitmap;
 import android.os.Handler;
 import android.os.Message;
+import android.text.TextUtils;
 
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.uicc.IccFileHandler;
 
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
+
 import static com.android.internal.telephony.cat.CatCmdMessage.
                    SetupEventListConstants.USER_ACTIVITY_EVENT;
 import static com.android.internal.telephony.cat.CatCmdMessage.
@@ -47,6 +50,8 @@
     private int mIconLoadState = LOAD_NO_ICON;
     private RilMessageDecoder mCaller = null;
     private boolean mloadIcon = false;
+    private String mSavedLanguage;
+    private String mRequestedLanguage;
 
     // constants
     static final int MSG_ID_LOAD_ICON_DONE = 1;
@@ -66,6 +71,10 @@
     static final int DTTZ_SETTING                           = 0x03;
     static final int LANGUAGE_SETTING                       = 0x04;
 
+    // Command Qualifier value for language notification command
+    static final int NON_SPECIFIC_LANGUAGE                  = 0x00;
+    static final int SPECIFIC_LANGUAGE                      = 0x01;
+
     // As per TS 102.223 Annex C, Structure of CAT communications,
     // the APDU length can be max 255 bytes. This leaves only 239 bytes for user
     // input string. CMD details TLV + Device IDs TLV + Result TLV + Other
@@ -203,6 +212,9 @@
              case PROVIDE_LOCAL_INFORMATION:
                 cmdPending = processProvideLocalInfo(cmdDet, ctlvs);
                 break;
+             case LANGUAGE_NOTIFICATION:
+                 cmdPending = processLanguageNotification(cmdDet, ctlvs);
+                 break;
              case OPEN_CHANNEL:
              case CLOSE_CHANNEL:
              case RECEIVE_DATA:
@@ -1014,6 +1026,67 @@
         return false;
     }
 
+    /**
+     * Processes LANGUAGE_NOTIFICATION proactive command from the SIM card.
+     *
+     * The SPECIFIC_LANGUAGE notification sets the specified language.
+     * The NON_SPECIFIC_LANGUAGE notification restores the last specifically set language.
+     *
+     * @param cmdDet Command Details object retrieved from the proactive command object
+     * @param ctlvs List of ComprehensionTlv objects following Command Details
+     *        object and Device Identities object within the proactive command
+     * @return false. This function always returns false meaning that the command
+     *         processing is  not pending and additional asynchronous processing
+     *         is not required.
+     */
+    private boolean processLanguageNotification(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
+            throws ResultException {
+        CatLog.d(this, "process Language Notification");
+
+        String desiredLanguage = null;
+        String currentLanguage = Locale.getDefault().getLanguage();
+        switch (cmdDet.commandQualifier) {
+            case NON_SPECIFIC_LANGUAGE:
+                if (!TextUtils.isEmpty(mSavedLanguage) && (!TextUtils.isEmpty(mRequestedLanguage)
+                        && mRequestedLanguage.equals(currentLanguage))) {
+                    CatLog.d(this, "Non-specific language notification changes the language "
+                            + "setting back to " + mSavedLanguage);
+                    desiredLanguage = mSavedLanguage;
+                }
+
+                mSavedLanguage = null;
+                mRequestedLanguage = null;
+                break;
+            case SPECIFIC_LANGUAGE:
+                ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.LANGUAGE, ctlvs);
+                if (ctlv != null) {
+                    int valueLen = ctlv.getLength();
+                    if (valueLen != 2) {
+                        throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
+                    }
+
+                    byte[] rawValue = ctlv.getRawValue();
+                    int valueIndex = ctlv.getValueIndex();
+                    desiredLanguage = GsmAlphabet.gsm8BitUnpackedToString(rawValue, valueIndex, 2);
+
+                    if (TextUtils.isEmpty(mSavedLanguage) || (!TextUtils.isEmpty(mRequestedLanguage)
+                            && !mRequestedLanguage.equals(currentLanguage))) {
+                        mSavedLanguage = currentLanguage;
+                    }
+                    mRequestedLanguage = desiredLanguage;
+                    CatLog.d(this, "Specific language notification changes the language setting to "
+                            + mRequestedLanguage);
+                }
+                break;
+            default:
+                CatLog.d(this, "LN[" + cmdDet.commandQualifier + "] Command Not Supported");
+                break;
+        }
+
+        mCmdParams = new LanguageParams(cmdDet, desiredLanguage);
+        return false;
+    }
+
     private boolean processBIPClient(CommandDetails cmdDet,
                                      List<ComprehensionTlv> ctlvs) throws ResultException {
         AppInterface.CommandType commandType =
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 9b86712..03843a7 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -101,9 +101,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Deque;
 import java.util.List;
 
 /**
@@ -1030,6 +1028,9 @@
                 break;
             case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE:
                 error = CommandException.Error.RADIO_NOT_AVAILABLE;
+                break;
+            case ImsReasonInfo.CODE_FDN_BLOCKED:
+                error = CommandException.Error.FDN_CHECK_FAILURE;
             default:
                 break;
         }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index b1013b1..b861b8f 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -16,6 +16,17 @@
 
 package com.android.internal.telephony.imsphone;
 
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_FAX;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_MAX;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PACKET;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.AsyncResult;
@@ -24,36 +35,24 @@
 import android.os.Message;
 import android.os.ResultReceiver;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
-import android.telephony.Rlog;
 
 import com.android.ims.ImsException;
 import com.android.ims.ImsReasonInfo;
 import com.android.ims.ImsSsInfo;
 import com.android.ims.ImsUtInterface;
 import com.android.internal.telephony.CallForwardInfo;
-import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.uicc.IccRecords;
-
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_FAX;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PACKET;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_MAX;
-
 import com.android.internal.telephony.MmiCode;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.uicc.IccRecords;
 
-import java.util.regex.Pattern;
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * The motto for this file is:
@@ -1148,6 +1147,14 @@
     }
 
     private CharSequence getErrorMessage(AsyncResult ar) {
+        if (ar.exception instanceof CommandException) {
+            CommandException.Error err = ((CommandException) (ar.exception)).getCommandError();
+            if (err == CommandException.Error.FDN_CHECK_FAILURE) {
+                Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE");
+                return mContext.getText(com.android.internal.R.string.mmiFdnError);
+            }
+        }
+
         return mContext.getText(com.android.internal.R.string.mmiError);
     }
 
@@ -1192,18 +1199,15 @@
                 if (err.getCommandError() == CommandException.Error.PASSWORD_INCORRECT) {
                     sb.append(mContext.getText(
                             com.android.internal.R.string.passwordIncorrect));
+                } else if (err.getCommandError() == CommandException.Error.FDN_CHECK_FAILURE) {
+                    sb.append(mContext.getText(com.android.internal.R.string.mmiFdnError));
                 } else if (err.getMessage() != null) {
                     sb.append(err.getMessage());
                 } else {
                     sb.append(mContext.getText(com.android.internal.R.string.mmiError));
                 }
-            } else {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+            } else if (ar.exception instanceof ImsException) {
+                sb.append(getImsErrorMessage(ar));
             }
         } else if (isActivate()) {
             mState = State.COMPLETE;
@@ -1336,12 +1340,7 @@
             mState = State.FAILED;
 
             if (ar.exception instanceof ImsException) {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+                sb.append(getImsErrorMessage(ar));
             }
             else {
                 sb.append(getErrorMessage(ar));
@@ -1397,21 +1396,14 @@
         StringBuilder sb = new StringBuilder(getScString());
         sb.append("\n");
 
+        mState = State.FAILED;
         if (ar.exception != null) {
-            mState = State.FAILED;
-
             if (ar.exception instanceof ImsException) {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+                sb.append(getImsErrorMessage(ar));
             } else {
                 sb.append(getErrorMessage(ar));
             }
         } else {
-            mState = State.FAILED;
             ImsSsInfo ssInfo = null;
             if (ar.result instanceof Bundle) {
                 Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received CLIP/COLP/COLR Response.");
@@ -1462,12 +1454,7 @@
             mState = State.FAILED;
 
             if (ar.exception instanceof ImsException) {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+                sb.append(getImsErrorMessage(ar));
             } else {
                 sb.append(getErrorMessage(ar));
             }
@@ -1501,14 +1488,8 @@
         mState = State.FAILED;
 
         if (ar.exception != null) {
-
             if (ar.exception instanceof ImsException) {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+                sb.append(getImsErrorMessage(ar));
             }
         } else {
             Bundle ssInfo = (Bundle) ar.result;
@@ -1599,12 +1580,7 @@
             mState = State.FAILED;
 
             if (ar.exception instanceof ImsException) {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+                sb.append(getImsErrorMessage(ar));
             } else {
                 sb.append(getErrorMessage(ar));
             }
@@ -1652,6 +1628,17 @@
         return sb;
     }
 
+    private CharSequence getImsErrorMessage(AsyncResult ar) {
+        ImsException error = (ImsException) ar.exception;
+        if (error.getCode() == ImsReasonInfo.CODE_FDN_BLOCKED) {
+            return mContext.getText(com.android.internal.R.string.mmiFdnError);
+        } else if (error.getMessage() != null) {
+            return error.getMessage();
+        } else {
+            return getErrorMessage(ar);
+        }
+    }
+
     @Override
     public ResultReceiver getUssdCallbackReceiver() {
         return this.mCallbackReceiver;