Merge "Enable emergency dialer shortcuts feature"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ac8d18a..ce774d8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -65,6 +65,7 @@
     <protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE" />
     <protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_RESET" />
     <protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE" />
+    <protected-broadcast android:name= "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED" />
     <protected-broadcast android:name= "com.android.internal.provider.action.VOICEMAIL_SMS_RECEIVED" />
     <protected-broadcast android:name= "com.android.intent.isim_refresh" />
     <protected-broadcast android:name= "com.android.ims.ACTION_RCS_SERVICE_AVAILABLE" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d6c5fd7..b3889dd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -530,6 +530,16 @@
 
     <!-- Mobile network 4G summary [CHAR LIMIT=80] -->
     <string name="enhanced_4g_lte_mode_summary">Use LTE services to improve voice and other communications (recommended)</string>
+    <string name="enhanced_4g_lte_mode_summary_o2">Use 4G services to improve voice and other communications (recommended)</string>
+    <!-- Carrier variant of Enhaced 4G LTE Mode summary.  [CHAR LIMIT=80] -->
+    <string-array name="enhanced_4g_lte_mode_sumary_variant">
+        <!-- 0: Default -->
+        <item>@string/enhanced_4g_lte_mode_summary</item>
+        <!-- 1: Verizon -->
+        <item>@string/enhanced_4g_lte_mode_summary</item>
+        <!-- 2: O2 UK -->
+        <item>@string/enhanced_4g_lte_mode_summary_o2</item>
+    </string-array>
 
     <!-- Mobile network settings screen, data enabling checkbox name -->
     <string name="data_enabled">Data enabled</string>
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index 125c01a..86babd5 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -1149,15 +1149,22 @@
                     CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT);
             CharSequence[] variantTitles = getContext().getResources()
                     .getTextArray(R.array.enhanced_4g_lte_mode_title_variant);
-            // Default index 0 indicates the default title string
+            CharSequence[] variantSumaries = getContext().getResources()
+                    .getTextArray(R.array.enhanced_4g_lte_mode_sumary_variant);
+            // Default index 0 indicates the default title/sumary string
             CharSequence enhanced4glteModeTitle = variantTitles[0];
+            CharSequence enhanced4glteModeSummary = variantSumaries[0];
             if (variant4glteTitleIndex >= 0 && variant4glteTitleIndex < variantTitles.length) {
                 enhanced4glteModeTitle = variantTitles[variant4glteTitleIndex];
             }
+            if (variant4glteTitleIndex >= 0 && variant4glteTitleIndex < variantSumaries.length) {
+                enhanced4glteModeSummary = variantSumaries[variant4glteTitleIndex];
+            }
 
             mOnlyAutoSelectInHomeNW = carrierConfig.getBoolean(
                     CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL);
             mButton4glte.setTitle(enhanced4glteModeTitle);
+            mButton4glte.setSummary(enhanced4glteModeSummary);
             mLteDataServicePref.setEnabled(hasActiveSubscriptions);
             Preference ps;
             ps = findPreference(BUTTON_CELL_BROADCAST_SETTINGS);
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 5401ddc..bf0ce15 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -60,6 +60,7 @@
 import android.telephony.CellInfoWcdma;
 import android.telephony.CellLocation;
 import android.telephony.ClientRequestStats;
+import android.telephony.ICellInfoCallback;
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.telephony.LocationAccessPolicy;
 import android.telephony.ModemActivityInfo;
@@ -82,6 +83,7 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
@@ -217,6 +219,8 @@
     private static final int EVENT_GET_CELL_LOCATION_DONE = 63;
     private static final int CMD_MODEM_REBOOT = 64;
     private static final int EVENT_CMD_MODEM_REBOOT_DONE = 65;
+    private static final int CMD_REQUEST_CELL_INFO_UPDATE = 66;
+    private static final int EVENT_REQUEST_CELL_INFO_UPDATE_DONE = 67;
 
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
@@ -1000,13 +1004,11 @@
                     request.result = ar.exception == null;
                     notifyRequester(request);
                     break;
-
                 case CMD_GET_ALL_CELL_INFO:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_GET_ALL_CELL_INFO_DONE, request);
                     request.phone.requestCellInfoUpdate(request.workSource, onCompleted);
                     break;
-
                 case EVENT_GET_ALL_CELL_INFO_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
@@ -1017,22 +1019,45 @@
                         request.notifyAll();
                     }
                     break;
-
-                case CMD_GET_CELL_LOCATION: {
+                case CMD_REQUEST_CELL_INFO_UPDATE:
+                    request = (MainThreadRequest) msg.obj;
+                    request.phone.requestCellInfoUpdate(request.workSource,
+                            obtainMessage(EVENT_REQUEST_CELL_INFO_UPDATE_DONE, request));
+                    break;
+                case EVENT_REQUEST_CELL_INFO_UPDATE_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    ICellInfoCallback cb = (ICellInfoCallback) request.argument;
+                    try {
+                        if (ar.exception != null) {
+                            // something went wrong... the response is null
+                            Log.e(LOG_TAG, "Exception retrieving CellInfo=" + ar.exception);
+                            cb.onCellInfo(null);
+                        } else if (ar.result == null) {
+                            // timeout occurred, so force the result to non-null "empty"
+                            Log.w(LOG_TAG, "Timeout Waiting for CellInfo!");
+                            cb.onCellInfo(new ArrayList<CellInfo>());
+                        } else {
+                            // use the result as returned
+                            cb.onCellInfo((List<CellInfo>) ar.result);
+                        }
+                    } catch (RemoteException re) {
+                        Log.w(LOG_TAG, "Discarded CellInfo due to Callback RemoteException");
+                    }
+                    break;
+                case CMD_GET_CELL_LOCATION:
                     request = (MainThreadRequest) msg.obj;
                     WorkSource ws = (WorkSource) request.argument;
                     Phone phone = getPhoneFromRequest(request);
                     phone.getCellLocation(ws, obtainMessage(EVENT_GET_CELL_LOCATION_DONE, request));
                     break;
-                }
-
-                case EVENT_GET_CELL_LOCATION_DONE: {
+                case EVENT_GET_CELL_LOCATION_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
                     if (ar.exception == null) {
                         request.result = ar.result;
                     } else {
-                        Phone phone = getPhoneFromRequest(request);
+                        phone = getPhoneFromRequest(request);
                         request.result = (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)
                                 ? new CdmaCellLocation() : new GsmCellLocation();
                     }
@@ -1041,18 +1066,14 @@
                         request.notifyAll();
                     }
                     break;
-                }
-
                 case CMD_MODEM_REBOOT:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_RESET_MODEM_CONFIG_DONE, request);
                     mPhone.rebootModem(onCompleted);
                     break;
-
                 case EVENT_CMD_MODEM_REBOOT_DONE:
                     handleNullReturnEvent(msg, "rebootModem");
                     break;
-
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -1178,10 +1199,19 @@
 
     /**
      * Same as {@link #sendRequestAsync(int)} except it takes an argument.
-     * @see {@link #sendRequest(int,Object)}
+     * @see {@link #sendRequest(int)}
      */
     private void sendRequestAsync(int command, Object argument) {
-        MainThreadRequest request = new MainThreadRequest(argument);
+        sendRequestAsync(command, argument, null, null);
+    }
+
+    /**
+     * Same as {@link #sendRequestAsync(int,Object)} except it takes a Phone and WorkSource.
+     * @see {@link #sendRequest(int,Object)}
+     */
+    private void sendRequestAsync(
+            int command, Object argument, Phone phone, WorkSource workSource) {
+        MainThreadRequest request = new MainThreadRequest(argument, phone, workSource);
         Message msg = mMainThreadHandler.obtainMessage(command, request);
         msg.sendToTarget();
     }
@@ -1915,6 +1945,14 @@
         return (neighbors.size()) > 0 ? neighbors : null;
     }
 
+    private List<CellInfo> getCachedCellInfo() {
+        List<CellInfo> cellInfos = new ArrayList<CellInfo>();
+        for (Phone phone : PhoneFactory.getPhones()) {
+            List<CellInfo> info = phone.getAllCellInfo();
+            if (info != null) cellInfos.addAll(info);
+        }
+        return cellInfos;
+    }
 
     @Override
     public List<CellInfo> getAllCellInfo(String callingPackage) {
@@ -1925,6 +1963,11 @@
             return null;
         }
 
+        final int targetSdk = getTargetSdk(callingPackage);
+        if (targetSdk >= android.os.Build.VERSION_CODES.Q) {
+            return getCachedCellInfo();
+        }
+
         if (DBG_LOC) log("getAllCellInfo: is active user");
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
@@ -1942,6 +1985,34 @@
     }
 
     @Override
+    public void requestCellInfoUpdate(int subId, ICellInfoCallback cb, String callingPackage) {
+        requestCellInfoUpdateInternal(
+                subId, cb, callingPackage, getWorkSource(Binder.getCallingUid()));
+    }
+
+    @Override
+    public void requestCellInfoUpdateWithWorkSource(
+            int subId, ICellInfoCallback cb, String callingPackage, WorkSource workSource) {
+        enforceModifyPermission();
+        requestCellInfoUpdateInternal(subId, cb, callingPackage, workSource);
+    }
+
+    private void requestCellInfoUpdateInternal(
+            int subId, ICellInfoCallback cb, String callingPackage, WorkSource workSource) {
+        mPhone.getContext().getSystemService(AppOpsManager.class)
+                .checkPackage(Binder.getCallingUid(), callingPackage);
+        if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
+                callingPackage, Binder.getCallingUid(), Binder.getCallingPid(), true)) {
+            return;
+        }
+
+        final Phone phone = getPhone(subId);
+        if (phone == null) throw new IllegalArgumentException("Invalid Subscription Id: " + subId);
+
+        sendRequestAsync(CMD_REQUEST_CELL_INFO_UPDATE, cb, phone, workSource);
+    }
+
+    @Override
     public void setCellInfoListRate(int rateInMillis) {
         enforceModifyPermission();
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
@@ -2905,6 +2976,98 @@
         }
     }
 
+    @Override
+    public void registerImsProvisioningChangedCallback(int subId, IImsConfigCallback callback) {
+        enforceReadPrivilegedPermission("registerImsProvisioningChangedCallback");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mPhone.getContext(), getSlotIndexOrException(subId))
+                    .getConfigInterface().addConfigCallback(callback);
+        } catch (ImsException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback) {
+        enforceReadPrivilegedPermission("unregisterImsProvisioningChangedCallback");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mPhone.getContext(), getSlotIndexOrException(subId))
+                    .getConfigInterface().removeConfigCallback(callback);
+        } catch (ImsException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public int getImsProvisioningInt(int subId, int key) {
+        enforceReadPrivilegedPermission("getImsProvisioningInt");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mPhone.getContext(), getSlotIndexOrException(subId))
+                    .getConfigInterface().getConfigInt(key);
+        } catch (ImsException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public String getImsProvisioningString(int subId, int key) {
+        enforceReadPrivilegedPermission("getImsProvisioningString");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mPhone.getContext(), getSlotIndexOrException(subId))
+                    .getConfigInterface().getConfigString(key);
+        } catch (ImsException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public int setImsProvisioningInt(int subId, int key, int value) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setImsProvisioningInt");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mPhone.getContext(), getSlotIndexOrException(subId))
+                    .getConfigInterface().setConfig(key, value);
+        } catch (ImsException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public int setImsProvisioningString(int subId, int key, String value) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setImsProvisioningString");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mPhone.getContext(), getSlotIndexOrException(subId))
+                    .getConfigInterface().setConfig(key, value);
+        } catch (ImsException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     private int getSlotIndexOrException(int subId) throws IllegalArgumentException {
         int slotId = SubscriptionManager.getSlotIndex(subId);
         if (!SubscriptionManager.isValidSlotIndex(slotId)) {