Merge changes from topic "refactor_pdcs"

* changes:
  Refactored precise data connection state
  Added hashcode for ApnSetting class
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 0a47032..a720601 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -26,10 +26,8 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.CallState;
 import android.telephony.Annotation.DataActivityType;
-import android.telephony.Annotation.DataFailureCause;
 import android.telephony.Annotation.DisconnectCauses;
 import android.telephony.Annotation.NetworkType;
 import android.telephony.Annotation.PreciseCallStates;
@@ -37,7 +35,6 @@
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SimActivationState;
 import android.telephony.Annotation.SrvccState;
-import android.telephony.data.ApnSetting;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
 import android.util.Log;
@@ -402,17 +399,16 @@
      * @param subId for which data connection state changed.
      * @param slotIndex for which data connections state changed. Can be derived from subId except
      * when subId is invalid.
-     * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
      * @param preciseState the PreciseDataConnectionState
      *
-     * @see android.telephony.PreciseDataConnection
+     * @see PreciseDataConnectionState
      * @see TelephonyManager#DATA_DISCONNECTED
      */
     public void notifyDataConnectionForSubscriber(int slotIndex, int subId,
-            @ApnType int apnType, @Nullable PreciseDataConnectionState preciseState) {
+            @NonNull PreciseDataConnectionState preciseState) {
         try {
             sRegistry.notifyDataConnectionForSubscriber(
-                    slotIndex, subId, apnType, preciseState);
+                    slotIndex, subId, preciseState);
         } catch (RemoteException ex) {
             // system process is dead
         }
@@ -612,25 +608,6 @@
     }
 
     /**
-     * Notify precise data connection failed cause on certain subscription.
-     *
-     * @param subId for which data connection failed.
-     * @param slotIndex for which data conenction failed. Can be derived from subId except when
-     * subId is invalid.
-     * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
-     * @param apn the APN {@link ApnSetting#getApnName()} of this data connection.
-     * @param failCause data fail cause.
-     */
-    public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, @ApnType int apnType,
-            @Nullable String apn, @DataFailureCause int failCause) {
-        try {
-            sRegistry.notifyPreciseDataConnectionFailed(slotIndex, subId, apnType, apn, failCause);
-        } catch (RemoteException ex) {
-            // system process is dead
-        }
-    }
-
-    /**
      * Notify single Radio Voice Call Continuity (SRVCC) state change for the currently active call
      * on certain subscription.
      *
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 5a04992..ea09fc8 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -65,9 +65,7 @@
     void notifyDataActivity(int state);
     void notifyDataActivityForSubscriber(in int subId, int state);
     void notifyDataConnectionForSubscriber(
-            int phoneId, int subId, int apnType, in PreciseDataConnectionState preciseState);
-    @UnsupportedAppUsage
-    void notifyDataConnectionFailed(String apnType);
+            int phoneId, int subId, in PreciseDataConnectionState preciseState);
     // Uses CellIdentity which is Parcelable here; will convert to CellLocation in client.
     void notifyCellLocation(in CellIdentity cellLocation);
     void notifyCellLocationForSubscriber(in int subId, in CellIdentity cellLocation);
@@ -77,8 +75,6 @@
             int foregroundCallState, int backgroundCallState);
     void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
             int preciseDisconnectCause);
-    void notifyPreciseDataConnectionFailed(int phoneId, int subId, int apnType, String apn,
-            int failCause);
     void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
     void notifySrvccStateChanged(in int subId, in int lteState);
     void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 7381da1..6d71c8e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -34,7 +34,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.net.LinkProperties;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -46,8 +45,6 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.telephony.Annotation;
-import android.telephony.Annotation.ApnType;
-import android.telephony.Annotation.DataFailureCause;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SrvccState;
 import android.telephony.BarringInfo;
@@ -80,7 +77,9 @@
 import android.telephony.data.ApnSetting;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
+import android.util.ArrayMap;
 import android.util.LocalLog;
+import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
@@ -103,6 +102,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 
 /**
  * Since phone process can be restarted, this class provides a centralized place
@@ -302,13 +302,18 @@
     @RadioPowerState
     private int mRadioPowerState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
 
-    private final LocalLog mLocalLog = new LocalLog(100);
+    private final LocalLog mLocalLog = new LocalLog(200);
 
-    private final LocalLog mListenLog = new LocalLog(100);
+    private final LocalLog mListenLog = new LocalLog(00);
 
-    // Per-phoneMap of APN Type to DataConnectionState
-    private List<Map<Integer, PreciseDataConnectionState>> mPreciseDataConnectionStates =
-            new ArrayList<Map<Integer, PreciseDataConnectionState>>();
+    /**
+     * Per-phone map of precise data connection state. The key of the map is the pair of transport
+     * type and APN setting. This is the cache to prevent redundant callbacks to the listeners.
+     * A precise data connection with state {@link TelephonyManager#DATA_DISCONNECTED} removes
+     * its entry from the map.
+     */
+    private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
+            mPreciseDataConnectionStates;
 
     static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_REGISTRATION_FAILURE
@@ -521,7 +526,7 @@
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
-            mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>());
+            mPreciseDataConnectionStates.add(new ArrayMap<>());
             mBarringInfo.add(i, new BarringInfo());
             mTelephonyDisplayInfos[i] = null;
         }
@@ -610,7 +615,7 @@
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
-            mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>());
+            mPreciseDataConnectionStates.add(new ArrayMap<>());
             mBarringInfo.add(i, new BarringInfo());
             mTelephonyDisplayInfos[i] = null;
         }
@@ -1687,38 +1692,25 @@
      *
      * @param phoneId the phoneId carrying the data connection
      * @param subId the subscriptionId for the data connection
-     * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
      * @param preciseState a PreciseDataConnectionState that has info about the data connection
      */
     @Override
-    public void notifyDataConnectionForSubscriber(
-            int phoneId, int subId, @ApnType int apnType, PreciseDataConnectionState preciseState) {
+    public void notifyDataConnectionForSubscriber(int phoneId, int subId,
+            @NonNull PreciseDataConnectionState preciseState) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
             return;
         }
 
-        String apn = "";
-        int state = TelephonyManager.DATA_UNKNOWN;
-        int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-        LinkProperties linkProps = null;
+        ApnSetting apnSetting = preciseState.getApnSetting();
 
-        if (preciseState != null) {
-            apn = preciseState.getDataConnectionApn();
-            state = preciseState.getState();
-            networkType = preciseState.getNetworkType();
-            linkProps = preciseState.getLinkProperties();
-        }
-        if (VDBG) {
-            log("notifyDataConnectionForSubscriber: subId=" + subId
-                    + " state=" + state + "' apn='" + apn
-                    + "' apnType=" + apnType + " networkType=" + networkType
-                    + "' preciseState=" + preciseState);
-        }
+        int apnTypes = apnSetting.getApnTypeBitmask();
+        int state = preciseState.getState();
+        int networkType = preciseState.getNetworkType();
 
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 // We only call the callback when the change is for default APN type.
-                if ((ApnSetting.TYPE_DEFAULT & apnType) != 0
+                if ((ApnSetting.TYPE_DEFAULT & apnTypes) != 0
                         && (mDataConnectionState[phoneId] != state
                         || mDataConnectionNetworkType[phoneId] != networkType)) {
                     String str = "onDataConnectionStateChanged("
@@ -1747,19 +1739,11 @@
                     mDataConnectionNetworkType[phoneId] = networkType;
                 }
 
-                boolean needsNotify = false;
-                // State has been cleared for this APN Type
-                if (preciseState == null) {
-                    // We try clear the state and check if the state was previously not cleared
-                    needsNotify = mPreciseDataConnectionStates.get(phoneId).remove(apnType) != null;
-                } else {
-                    // We need to check to see if the state actually changed
-                    PreciseDataConnectionState oldPreciseState =
-                            mPreciseDataConnectionStates.get(phoneId).put(apnType, preciseState);
-                    needsNotify = !preciseState.equals(oldPreciseState);
-                }
-
-                if (needsNotify) {
+                Pair<Integer, ApnSetting> key = Pair.create(preciseState.getTransportType(),
+                        preciseState.getApnSetting());
+                PreciseDataConnectionState oldState = mPreciseDataConnectionStates.get(phoneId)
+                        .remove(key);
+                if (!Objects.equals(oldState, preciseState)) {
                     for (Record r : mRecords) {
                         if (r.matchPhoneStateListenerEvent(
                                 PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
@@ -1771,54 +1755,22 @@
                             }
                         }
                     }
+                    handleRemoveListLocked();
+
+                    broadcastDataConnectionStateChanged(phoneId, subId, preciseState);
+
+                    String str = "notifyDataConnectionForSubscriber: phoneId=" + phoneId + " subId="
+                            + subId + " " + preciseState;
+                    log(str);
+                    mLocalLog.log(str);
+                }
+
+                // If the state is disconnected, it would be the end of life cycle of a data
+                // connection, so remove it from the cache.
+                if (preciseState.getState() != TelephonyManager.DATA_DISCONNECTED) {
+                    mPreciseDataConnectionStates.get(phoneId).put(key, preciseState);
                 }
             }
-            handleRemoveListLocked();
-        }
-
-        broadcastDataConnectionStateChanged(state, apn, apnType, subId);
-    }
-
-    /**
-     * Stub to satisfy the ITelephonyRegistry aidl interface; do not use this function.
-     * @see #notifyDataConnectionFailedForSubscriber
-     */
-    public void notifyDataConnectionFailed(String apnType) {
-        loge("This function should not be invoked");
-    }
-
-    private void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, int apnType) {
-        if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
-            return;
-        }
-        if (VDBG) {
-            log("notifyDataConnectionFailedForSubscriber: subId=" + subId
-                    + " apnType=" + apnType);
-        }
-        synchronized (mRecords) {
-            if (validatePhoneId(phoneId)) {
-                mPreciseDataConnectionStates.get(phoneId).put(
-                        apnType,
-                        new PreciseDataConnectionState.Builder()
-                                .setApnSetting(new ApnSetting.Builder()
-                                        .setApnTypeBitmask(apnType)
-                                        .build())
-                                .build());
-                for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
-                            && idMatch(r.subId, subId, phoneId)) {
-                        try {
-                            r.callback.onPreciseDataConnectionStateChanged(
-                                    mPreciseDataConnectionStates.get(phoneId).get(apnType));
-                        } catch (RemoteException ex) {
-                            mRemoveList.add(r.binder);
-                        }
-                    }
-                }
-            }
-
-            handleRemoveListLocked();
         }
     }
 
@@ -1972,43 +1924,6 @@
     }
 
     @Override
-    public void notifyPreciseDataConnectionFailed(int phoneId, int subId, @ApnType int apnType,
-            String apn, @DataFailureCause int failCause) {
-        if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
-            return;
-        }
-
-        // precise notify invokes imprecise notify
-        notifyDataConnectionFailedForSubscriber(phoneId, subId, apnType);
-
-        synchronized (mRecords) {
-            if (validatePhoneId(phoneId)) {
-                mPreciseDataConnectionStates.get(phoneId).put(
-                        apnType,
-                        new PreciseDataConnectionState.Builder()
-                                .setApnSetting(new ApnSetting.Builder()
-                                        .setApnTypeBitmask(apnType)
-                                        .build())
-                                .setFailCause(failCause)
-                                .build());
-                for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
-                            && idMatch(r.subId, subId, phoneId)) {
-                        try {
-                            r.callback.onPreciseDataConnectionStateChanged(
-                                    mPreciseDataConnectionStates.get(phoneId).get(apnType));
-                        } catch (RemoteException ex) {
-                            mRemoveList.add(r.binder);
-                        }
-                    }
-                }
-            }
-            handleRemoveListLocked();
-        }
-    }
-
-    @Override
     public void notifySrvccStateChanged(int subId, @SrvccState int state) {
         if (!checkNotifyPermission("notifySrvccStateChanged()")) {
             return;
@@ -2578,16 +2493,18 @@
         }
     }
 
-    private void broadcastDataConnectionStateChanged(int state, String apn,
-                                                     int apnType, int subId) {
+    private void broadcastDataConnectionStateChanged(int slotIndex, int subId,
+            @NonNull PreciseDataConnectionState pdcs) {
         // Note: not reporting to the battery stats service here, because the
         // status bar takes care of that after taking into account all of the
         // required info.
         Intent intent = new Intent(ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
-        intent.putExtra(PHONE_CONSTANTS_STATE_KEY, TelephonyUtils.dataStateToString(state));
-        intent.putExtra(PHONE_CONSTANTS_DATA_APN_KEY, apn);
+        intent.putExtra(PHONE_CONSTANTS_STATE_KEY,
+                TelephonyUtils.dataStateToString(pdcs.getState()));
+        intent.putExtra(PHONE_CONSTANTS_DATA_APN_KEY, pdcs.getApnSetting().getApnName());
         intent.putExtra(PHONE_CONSTANTS_DATA_APN_TYPE_KEY,
-                ApnSetting.getApnTypesStringFromBitmask(apnType));
+                ApnSetting.getApnTypesStringFromBitmask(pdcs.getApnSetting().getApnTypeBitmask()));
+        intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, slotIndex);
         intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE);
     }
@@ -2973,7 +2890,7 @@
     /**
      * Returns a string representation of the radio technology (network type)
      * currently in use on the device.
-     * @param subId for which network type is returned
+     * @param type for which network type is returned
      * @return the name of the radio technology
      *
      */
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index ecef747..fd9f460 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -36,6 +36,8 @@
 import android.telephony.data.ApnSetting;
 import android.telephony.data.DataCallResponse;
 
+import com.android.internal.telephony.util.TelephonyUtils;
+
 import java.util.Objects;
 
 
@@ -82,6 +84,7 @@
                 linkProperties, failCause, new ApnSetting.Builder()
                         .setApnTypeBitmask(apnTypes)
                         .setApnName(apn)
+                        .setEntryName(apn)
                         .build());
     }
 
@@ -254,7 +257,9 @@
     /**
      * Return the APN Settings for this data connection.
      *
-     * @return the ApnSetting that was used to configure this data connection.
+     * @return the ApnSetting that was used to configure this data connection. Note that a data
+     * connection cannot be established without a valid {@link ApnSetting}. The return value would
+     * never be {@code null} even though it has {@link Nullable} annotation.
      */
     public @Nullable ApnSetting getApnSetting() {
         return mApnSetting;
@@ -314,17 +319,14 @@
     public String toString() {
         StringBuilder sb = new StringBuilder();
 
-        sb.append("Data Connection state: " + mState);
-        sb.append(", Transport type: "
+        sb.append(" state: " + TelephonyUtils.dataStateToString(mState));
+        sb.append(", transport: "
                 + AccessNetworkConstants.transportTypeToString(mTransportType));
-        sb.append(", Id: " + mId);
-        sb.append(", Network type: " + mNetworkType);
-        sb.append(", APN types: " + ApnSetting.getApnTypesStringFromBitmask(
-                getDataConnectionApnTypeBitMask()));
-        sb.append(", APN: " + getDataConnectionApn());
-        sb.append(", Link properties: " + mLinkProperties);
-        sb.append(", Fail cause: " + DataFailCause.toString(mFailCause));
-        sb.append(", Apn Setting: " + mApnSetting);
+        sb.append(", id: " + mId);
+        sb.append(", network type: " + TelephonyManager.getNetworkTypeName(mNetworkType));
+        sb.append(", APN Setting: " + mApnSetting);
+        sb.append(", link properties: " + mLinkProperties);
+        sb.append(", fail cause: " + DataFailCause.toString(mFailCause));
 
         return sb.toString();
     }
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index e60ae89..ff9329e 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1214,12 +1214,16 @@
         return false;
     }
 
-    // TODO - if we have this function we should also have hashCode.
-    // Also should handle changes in type order and perhaps case-insensitivity.
+    @Override
+    public int hashCode() {
+        return Objects.hash(mApnName, mProxyAddress, mProxyPort, mMmsc, mMmsProxyAddress,
+                mMmsProxyPort, mUser, mPassword, mAuthType, mApnTypeBitmask, mId, mOperatorNumeric,
+                mProtocol, mRoamingProtocol, mMtu, mCarrierEnabled, mNetworkTypeBitmask, mProfileId,
+                mPersistent, mMaxConns, mWaitTime, mMaxConnsTime, mMvnoType, mMvnoMatchData,
+                mApnSetId, mCarrierId, mSkip464Xlat);
+    }
 
-    /**
-     * @hide
-     */
+    @Override
     public boolean equals(Object o) {
         if (o instanceof ApnSetting == false) {
             return false;