Merge "Add ITelephonyDebugSubscriber.aidl" into mm-wireless-dev
diff --git a/api/current.txt b/api/current.txt
index 19e58ae..3691270 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19218,6 +19218,7 @@
     method public java.lang.String getAltSubjectMatch();
     method public java.lang.String getAnonymousIdentity();
     method public java.security.cert.X509Certificate getCaCertificate();
+    method public java.security.cert.X509Certificate[] getCaCertificates();
     method public java.security.cert.X509Certificate getClientCertificate();
     method public java.lang.String getDomainSuffixMatch();
     method public int getEapMethod();
@@ -19230,6 +19231,7 @@
     method public void setAltSubjectMatch(java.lang.String);
     method public void setAnonymousIdentity(java.lang.String);
     method public void setCaCertificate(java.security.cert.X509Certificate);
+    method public void setCaCertificates(java.security.cert.X509Certificate[]);
     method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
     method public void setDomainSuffixMatch(java.lang.String);
     method public void setEapMethod(int);
@@ -30001,6 +30003,7 @@
     method public static boolean hasProperty(int, int);
     method public boolean hasProperty(int);
     method public static java.lang.String propertiesToString(int);
+    field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
     field public static final int CAPABILITY_HOLD = 1; // 0x1
@@ -30140,6 +30143,7 @@
     method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
     method public final void setVideoState(int);
     method public static java.lang.String stateToString(int);
+    field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -30673,6 +30677,8 @@
 
   public final class CellIdentityGsm implements android.os.Parcelable {
     method public int describeContents();
+    method public int getArfcn();
+    method public int getBsic();
     method public int getCid();
     method public int getLac();
     method public int getMcc();
@@ -30685,6 +30691,7 @@
   public final class CellIdentityLte implements android.os.Parcelable {
     method public int describeContents();
     method public int getCi();
+    method public int getEarfcn();
     method public int getMcc();
     method public int getMnc();
     method public int getPci();
@@ -30700,6 +30707,7 @@
     method public int getMcc();
     method public int getMnc();
     method public int getPsc();
+    method public int getUarfcn();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityWcdma> CREATOR;
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 20c0560..b09a4a2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -20994,6 +20994,7 @@
     method public java.lang.String getAltSubjectMatch();
     method public java.lang.String getAnonymousIdentity();
     method public java.security.cert.X509Certificate getCaCertificate();
+    method public java.security.cert.X509Certificate[] getCaCertificates();
     method public java.security.cert.X509Certificate getClientCertificate();
     method public java.lang.String getDomainSuffixMatch();
     method public int getEapMethod();
@@ -21006,6 +21007,7 @@
     method public void setAltSubjectMatch(java.lang.String);
     method public void setAnonymousIdentity(java.lang.String);
     method public void setCaCertificate(java.security.cert.X509Certificate);
+    method public void setCaCertificates(java.security.cert.X509Certificate[]);
     method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
     method public void setDomainSuffixMatch(java.lang.String);
     method public void setEapMethod(int);
@@ -32177,6 +32179,7 @@
     method public static boolean hasProperty(int, int);
     method public boolean hasProperty(int);
     method public static java.lang.String propertiesToString(int);
+    field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
     field public static final int CAPABILITY_HOLD = 1; // 0x1
@@ -32327,6 +32330,7 @@
     method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
     method public final void setVideoState(int);
     method public static java.lang.String stateToString(int);
+    field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -32920,6 +32924,8 @@
 
   public final class CellIdentityGsm implements android.os.Parcelable {
     method public int describeContents();
+    method public int getArfcn();
+    method public int getBsic();
     method public int getCid();
     method public int getLac();
     method public int getMcc();
@@ -32932,6 +32938,7 @@
   public final class CellIdentityLte implements android.os.Parcelable {
     method public int describeContents();
     method public int getCi();
+    method public int getEarfcn();
     method public int getMcc();
     method public int getMnc();
     method public int getPci();
@@ -32947,6 +32954,7 @@
     method public int getMcc();
     method public int getMnc();
     method public int getPsc();
+    method public int getUarfcn();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityWcdma> CREATOR;
   }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 3bd12c0..e27c0fb 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -197,6 +197,19 @@
             (1 << NET_CAPABILITY_CAPTIVE_PORTAL);
 
     /**
+     * Network specifier for factories which want to match any network specifier
+     * (NS) in a request. Behavior:
+     * <li>Empty NS in request matches any network factory NS</li>
+     * <li>Empty NS in the network factory NS only matches a request with an
+     * empty NS</li>
+     * <li>"*" (this constant) NS in the network factory matches requests with
+     * any NS</li>
+     *
+     * @hide
+     */
+    public static final String MATCH_ALL_REQUESTS_NETWORK_SPECIFIER = "*";
+
+    /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
      * NetworkFactory / NetworkAgent model does not deal well with the situation where a
      * capability's presence cannot be known in advance. If such a capability is requested, then we
@@ -596,7 +609,8 @@
     }
     private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
         return (TextUtils.isEmpty(mNetworkSpecifier) ||
-                mNetworkSpecifier.equals(nc.mNetworkSpecifier));
+                mNetworkSpecifier.equals(nc.mNetworkSpecifier) ||
+                MATCH_ALL_REQUESTS_NETWORK_SPECIFIER.equals(nc.mNetworkSpecifier));
     }
     private boolean equalsSpecifier(NetworkCapabilities nc) {
         if (TextUtils.isEmpty(mNetworkSpecifier)) {
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 7da4818..f1edcbe 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -188,6 +188,10 @@
          *                         networks.
          */
         public Builder setNetworkSpecifier(String networkSpecifier) {
+            if (NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER.equals(networkSpecifier)) {
+                throw new IllegalArgumentException("Invalid network specifier - must not be '"
+                        + NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER + "'");
+            }
             mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
             return this;
         }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 327fb8a..9c09f24 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3678,6 +3678,12 @@
             throw new IllegalArgumentException("Bad timeout specified");
         }
 
+        if (NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER
+                .equals(networkCapabilities.getNetworkSpecifier())) {
+            throw new IllegalArgumentException("Invalid network specifier - must not be '"
+                    + NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER + "'");
+        }
+
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId());
         NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 97e16da..d096c28 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -52,13 +52,16 @@
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
+import android.os.Messenger;
 import android.os.MessageQueue.IdleHandler;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import android.util.LogPrinter;
 
@@ -1291,6 +1294,36 @@
         validatedCallback.expectCallback(CallbackState.LOST);
     }
 
+    @SmallTest
+    public void testInvalidNetworkSpecifier() {
+        boolean execptionCalled = true;
+
+        try {
+            NetworkRequest.Builder builder = new NetworkRequest.Builder();
+            builder.setNetworkSpecifier(MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
+            execptionCalled = false;
+        } catch (IllegalArgumentException e) {
+            // do nothing - should get here
+        }
+
+        assertTrue("NetworkReqeuest builder with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER",
+                execptionCalled);
+
+        try {
+            NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+            networkCapabilities.addTransportType(TRANSPORT_WIFI)
+                    .setNetworkSpecifier(NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
+            mService.requestNetwork(networkCapabilities, null, 0, null,
+                    ConnectivityManager.TYPE_WIFI);
+            execptionCalled = false;
+        } catch (IllegalArgumentException e) {
+            // do nothing - should get here
+        }
+
+        assertTrue("ConnectivityService requestNetwork with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER",
+                execptionCalled);
+    }
+
     private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
 
         public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 6511705..270c8ce 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -24,7 +24,6 @@
 import java.lang.String;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -210,7 +209,22 @@
          * Call sends responses through connection.
          * @hide
          */
-        public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
+        public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00200000;
+
+        /**
+         * When set, prevents a video {@code Call} from being downgraded to an audio-only call.
+         * <p>
+         * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
+         * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
+         * downgraded from a video call back to a VideoState of
+         * {@link VideoProfile#STATE_AUDIO_ONLY}.
+         * <p>
+         * Intuitively, a call which can be downgraded to audio should also have local and remote
+         * video
+         * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
+         * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
+         */
+        public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000;
 
         //******************************************************************************************
         // Next CAPABILITY value: 0x00800000
@@ -327,6 +341,9 @@
             if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
                 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
             }
+            if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
+                builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
+            }
             if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
                 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
             }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 4115756..c9fc51e 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -255,8 +255,23 @@
      */
     public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
 
+    /**
+     * When set, prevents a video call from being downgraded to an audio-only call.
+     * <p>
+     * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
+     * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
+     * downgraded from a video call back to a VideoState of
+     * {@link VideoProfile#STATE_AUDIO_ONLY}.
+     * <p>
+     * Intuitively, a call which can be downgraded to audio should also have local and remote
+     * video
+     * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
+     * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
+     */
+    public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
+
     //**********************************************************************************************
-    // Next CAPABILITY value: 0x00800000
+    // Next CAPABILITY value: 0x01000000
     //**********************************************************************************************
 
     /**
@@ -371,6 +386,9 @@
         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
             builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
         }
+        if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
+            builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
+        }
         if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
             builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
         }
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 90d2aa0..74f1171 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -38,6 +38,10 @@
     private final int mLac;
     // 16-bit GSM Cell Identity described in TS 27.007, 0..65535
     private final int mCid;
+    // 16-bit GSM Absolute RF Channel Number
+    private final int mArfcn;
+    // 6-bit Base Station Identity Code
+    private final int mBsic;
 
     /**
      * @hide
@@ -47,6 +51,8 @@
         mMnc = Integer.MAX_VALUE;
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
+        mArfcn = Integer.MAX_VALUE;
+        mBsic = Integer.MAX_VALUE;
     }
     /**
      * public constructor
@@ -58,10 +64,27 @@
      * @hide
      */
     public CellIdentityGsm (int mcc, int mnc, int lac, int cid) {
+        this(mcc, mnc, lac, cid, Integer.MAX_VALUE, Integer.MAX_VALUE);
+    }
+
+    /**
+     * public constructor
+     * @param mcc 3-digit Mobile Country Code, 0..999
+     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+     * @param lac 16-bit Location Area Code, 0..65535
+     * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
+     * @param arfcn 16-bit GSM Absolute RF Channel Number
+     * @param bsic 6-bit Base Station Identity Code
+     *
+     * @hide
+     */
+    public CellIdentityGsm (int mcc, int mnc, int lac, int cid, int arfcn, int bsic) {
         mMcc = mcc;
         mMnc = mnc;
         mLac = lac;
         mCid = cid;
+        mArfcn = arfcn;
+        mBsic = bsic;
     }
 
     private CellIdentityGsm(CellIdentityGsm cid) {
@@ -69,6 +92,8 @@
         mMnc = cid.mMnc;
         mLac = cid.mLac;
         mCid = cid.mCid;
+        mArfcn = cid.mArfcn;
+        mBsic = cid.mBsic;
     }
 
     CellIdentityGsm copy() {
@@ -106,6 +131,21 @@
     }
 
     /**
+     * @return 16-bit GSM Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+     */
+    public int getArfcn() {
+        return mArfcn;
+    }
+
+    /**
+     * @return 6-bit Base Station Identity Code, Integer.MAX_VALUE if unknown
+     */
+    public int getBsic() {
+        return mBsic;
+    }
+
+
+    /**
      * @return Integer.MAX_VALUE, undefined for GSM
      */
     @Deprecated
@@ -132,7 +172,9 @@
         return mMcc == o.mMcc &&
                 mMnc == o.mMnc &&
                 mLac == o.mLac &&
-                mCid == o.mCid;
+                mCid == o.mCid &&
+                mArfcn == o.mArfcn &&
+                mBsic == o.mBsic;
     }
 
     @Override
@@ -142,6 +184,8 @@
         sb.append(" mMnc=").append(mMnc);
         sb.append(" mLac=").append(mLac);
         sb.append(" mCid=").append(mCid);
+        sb.append(" mArfcn=").append(mArfcn);
+        sb.append(" mBsic=").append("0x").append(Integer.toHexString(mBsic));
         sb.append("}");
 
         return sb.toString();
@@ -161,6 +205,8 @@
         dest.writeInt(mMnc);
         dest.writeInt(mLac);
         dest.writeInt(mCid);
+        dest.writeInt(mArfcn);
+        dest.writeInt(mBsic);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -169,6 +215,8 @@
         mMnc = in.readInt();
         mLac = in.readInt();
         mCid = in.readInt();
+        mArfcn = in.readInt();
+        mBsic = in.readInt();
         if (DBG) log("CellIdentityGsm(Parcel): " + toString());
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 1e7ac08..ce74383 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -40,6 +40,8 @@
     private final int mPci;
     // 16-bit tracking area code
     private final int mTac;
+    // 18-bit Absolute RF Channel Number
+    private final int mEarfcn;
 
     /**
      * @hide
@@ -50,6 +52,7 @@
         mCi = Integer.MAX_VALUE;
         mPci = Integer.MAX_VALUE;
         mTac = Integer.MAX_VALUE;
+        mEarfcn = Integer.MAX_VALUE;
     }
 
     /**
@@ -63,11 +66,27 @@
      * @hide
      */
     public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac) {
+        this(mcc, mnc, ci, pci, tac, Integer.MAX_VALUE);
+    }
+
+    /**
+     *
+     * @param mcc 3-digit Mobile Country Code, 0..999
+     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+     * @param ci 28-bit Cell Identity
+     * @param pci Physical Cell Id 0..503
+     * @param tac 16-bit Tracking Area Code
+     * @param earfcn 18-bit LTE Absolute RF Channel Number
+     *
+     * @hide
+     */
+    public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
         mMcc = mcc;
         mMnc = mnc;
         mCi = ci;
         mPci = pci;
         mTac = tac;
+        mEarfcn = earfcn;
     }
 
     private CellIdentityLte(CellIdentityLte cid) {
@@ -76,6 +95,7 @@
         mCi = cid.mCi;
         mPci = cid.mPci;
         mTac = cid.mTac;
+        mEarfcn = cid.mEarfcn;
     }
 
     CellIdentityLte copy() {
@@ -117,6 +137,13 @@
         return mTac;
     }
 
+    /**
+     * @return 18-bit Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+     */
+    public int getEarfcn() {
+        return mEarfcn;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(mMcc, mMnc, mCi, mPci, mTac);
@@ -137,7 +164,8 @@
                 mMnc == o.mMnc &&
                 mCi == o.mCi &&
                 mPci == o.mPci &&
-                mTac == o.mTac;
+                mTac == o.mTac &&
+                mEarfcn == o.mEarfcn;
     }
 
     @Override
@@ -148,6 +176,7 @@
         sb.append(" mCi="); sb.append(mCi);
         sb.append(" mPci="); sb.append(mPci);
         sb.append(" mTac="); sb.append(mTac);
+        sb.append(" mEarfcn="); sb.append(mEarfcn);
         sb.append("}");
 
         return sb.toString();
@@ -168,6 +197,7 @@
         dest.writeInt(mCi);
         dest.writeInt(mPci);
         dest.writeInt(mTac);
+        dest.writeInt(mEarfcn);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -177,6 +207,7 @@
         mCi = in.readInt();
         mPci = in.readInt();
         mTac = in.readInt();
+        mEarfcn = in.readInt();
         if (DBG) log("CellIdentityLte(Parcel): " + toString());
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 56ee8c9..0d13efd 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -40,6 +40,8 @@
     private final int mCid;
     // 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511
     private final int mPsc;
+    // 16-bit UMTS Absolute RF Channel Number
+    private final int mUarfcn;
 
     /**
      * @hide
@@ -50,6 +52,7 @@
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mPsc = Integer.MAX_VALUE;
+        mUarfcn = Integer.MAX_VALUE;
     }
     /**
      * public constructor
@@ -62,11 +65,27 @@
      * @hide
      */
     public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc) {
+        this(mcc, mnc, lac, cid, psc, Integer.MAX_VALUE);
+    }
+
+    /**
+     * public constructor
+     * @param mcc 3-digit Mobile Country Code, 0..999
+     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+     * @param lac 16-bit Location Area Code, 0..65535
+     * @param cid 28-bit UMTS Cell Identity
+     * @param psc 9-bit UMTS Primary Scrambling Code
+     * @param uarfcn 16-bit UMTS Absolute RF Channel Number
+     *
+     * @hide
+     */
+    public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc, int uarfcn) {
         mMcc = mcc;
         mMnc = mnc;
         mLac = lac;
         mCid = cid;
         mPsc = psc;
+        mUarfcn = uarfcn;
     }
 
     private CellIdentityWcdma(CellIdentityWcdma cid) {
@@ -75,6 +94,7 @@
         mLac = cid.mLac;
         mCid = cid.mCid;
         mPsc = cid.mPsc;
+        mUarfcn = cid.mUarfcn;
     }
 
     CellIdentityWcdma copy() {
@@ -123,6 +143,13 @@
         return Objects.hash(mMcc, mMnc, mLac, mCid, mPsc);
     }
 
+    /**
+     * @return 16-bit UMTS Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+     */
+    public int getUarfcn() {
+        return mUarfcn;
+    }
+
     @Override
     public boolean equals(Object other) {
         if (this == other) {
@@ -138,7 +165,8 @@
                 mMnc == o.mMnc &&
                 mLac == o.mLac &&
                 mCid == o.mCid &&
-                mPsc == o.mPsc;
+                mPsc == o.mPsc &&
+                mUarfcn == o.mUarfcn;
     }
 
     @Override
@@ -149,6 +177,7 @@
         sb.append(" mLac=").append(mLac);
         sb.append(" mCid=").append(mCid);
         sb.append(" mPsc=").append(mPsc);
+        sb.append(" mUarfcn=").append(mUarfcn);
         sb.append("}");
 
         return sb.toString();
@@ -169,6 +198,7 @@
         dest.writeInt(mLac);
         dest.writeInt(mCid);
         dest.writeInt(mPsc);
+        dest.writeInt(mUarfcn);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -178,6 +208,7 @@
         mLac = in.readInt();
         mCid = in.readInt();
         mPsc = in.readInt();
+        mUarfcn = in.readInt();
         if (DBG) log("CellIdentityWcdma(Parcel): " + toString());
     }
 
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index d27fcec..addf7ef 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -34,6 +34,7 @@
 
     private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
     private int mBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+    private int mTimingAdvance;
 
     /**
      * Empty constructor
@@ -75,6 +76,22 @@
     public void initialize(int ss, int ber) {
         mSignalStrength = ss;
         mBitErrorRate = ber;
+        mTimingAdvance = Integer.MAX_VALUE;
+    }
+
+    /**
+     * Initialize all the values
+     *
+     * @param ss SignalStrength as ASU value
+     * @param ber is Bit Error Rate
+     * @param ta timing advance
+     *
+     * @hide
+     */
+    public void initialize(int ss, int ber, int ta) {
+        mSignalStrength = ss;
+        mBitErrorRate = ber;
+        mTimingAdvance = ta;
     }
 
     /**
@@ -83,6 +100,7 @@
     protected void copyFrom(CellSignalStrengthGsm s) {
         mSignalStrength = s.mSignalStrength;
         mBitErrorRate = s.mBitErrorRate;
+        mTimingAdvance = s.mTimingAdvance;
     }
 
     /**
@@ -98,6 +116,7 @@
     public void setDefaultValues() {
         mSignalStrength = Integer.MAX_VALUE;
         mBitErrorRate = Integer.MAX_VALUE;
+        mTimingAdvance = Integer.MAX_VALUE;
     }
 
     /**
@@ -174,7 +193,8 @@
             return false;
         }
 
-        return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate;
+        return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate &&
+                        s.mTimingAdvance == mTimingAdvance;
     }
 
     /**
@@ -184,7 +204,8 @@
     public String toString() {
         return "CellSignalStrengthGsm:"
                 + " ss=" + mSignalStrength
-                + " ber=" + mBitErrorRate;
+                + " ber=" + mBitErrorRate
+                + " mTa=" + mTimingAdvance;
     }
 
     /** Implement the Parcelable interface */
@@ -193,6 +214,7 @@
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
         dest.writeInt(mSignalStrength);
         dest.writeInt(mBitErrorRate);
+        dest.writeInt(mTimingAdvance);
     }
 
     /**
@@ -202,6 +224,7 @@
     private CellSignalStrengthGsm(Parcel in) {
         mSignalStrength = in.readInt();
         mBitErrorRate = in.readInt();
+        mTimingAdvance = in.readInt();
         if (DBG) log("CellSignalStrengthGsm(Parcel): " + toString());
     }
 
diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
index 96069213..f71f58d 100644
--- a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
+++ b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
@@ -28,10 +28,10 @@
 public class DataConnectionRealTimeInfo implements Parcelable {
     private long mTime;             // Time the info was collected since boot in nanos;
 
-    public static int DC_POWER_STATE_LOW       = 1;
-    public static int DC_POWER_STATE_MEDIUM    = 2;
-    public static int DC_POWER_STATE_HIGH      = 3;
-    public static int DC_POWER_STATE_UNKNOWN   = Integer.MAX_VALUE;
+    public static final int DC_POWER_STATE_LOW       = 1;
+    public static final int DC_POWER_STATE_MEDIUM    = 2;
+    public static final int DC_POWER_STATE_HIGH      = 3;
+    public static final int DC_POWER_STATE_UNKNOWN   = Integer.MAX_VALUE;
 
     private int mDcPowerState;      // DC_POWER_STATE_[LOW | MEDIUM | HIGH | UNKNOWN]
 
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index b089387..962a600 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1857,9 +1857,6 @@
         // to the list.
         number = extractNetworkPortionAlt(number);
 
-        Rlog.d(LOG_TAG, "subId:" + subId + ", defaultCountryIso:" +
-                ((defaultCountryIso == null) ? "NULL" : defaultCountryIso));
-
         String emergencyNumbers = "";
         int slotId = SubscriptionManager.getSlotId(subId);
 
@@ -1869,7 +1866,8 @@
 
         emergencyNumbers = SystemProperties.get(ecclist, "");
 
-        Rlog.d(LOG_TAG, "slotId:" + slotId + ", emergencyNumbers: " +  emergencyNumbers);
+        Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:"
+                + defaultCountryIso + " emergencyNumbers: " +  emergencyNumbers);
 
         if (TextUtils.isEmpty(emergencyNumbers)) {
             // then read-only ecclist property since old RIL only uses this
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index c680999..ad007c6 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -37,6 +37,7 @@
 
     static final String LOG_TAG = "PHONE";
     static final boolean DBG = true;
+    static final boolean VDBG = false;  // STOPSHIP if true
 
     /**
      * Normal operation condition, the phone is registered
@@ -829,7 +830,7 @@
     /** @hide */
     public void setDataRegState(int state) {
         mDataRegState = state;
-        if (DBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
+        if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
     }
 
     public void setRoaming(boolean roaming) {
@@ -1017,7 +1018,8 @@
     /** @hide */
     public void setRilDataRadioTechnology(int rt) {
         this.mRilDataRadioTechnology = rt;
-        if (DBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRadioTechnology=" + mRilDataRadioTechnology);
+        if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setRilDataRadioTechnology=" +
+                mRilDataRadioTechnology);
     }
 
     /** @hide */
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 3c4c04b..7d5645e 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -336,6 +336,8 @@
     int RIL_REQUEST_PULL_LCEDATA = 134;
     int RIL_REQUEST_GET_ACTIVITY_INFO = 135;
 
+    int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
+
     int RIL_UNSOL_RESPONSE_BASE = 1000;
     int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
     int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001;
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 8838f3f..c6354bd 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -703,24 +703,6 @@
      */
     public int numUserTriggeredJoinAttempts;
 
-    /**
-     * @hide
-     * Connect choices
-     *
-     * remember the keys identifying the known WifiConfiguration over which this configuration
-     * was preferred by user or a "WiFi Network Management app", that is,
-     * a WifiManager.CONNECT_NETWORK or SELECT_NETWORK was received while this configuration
-     * was visible to the user:
-     * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP
-     *
-     * The integer represents the configuration's RSSI at that time (useful?)
-     *
-     * The overall auto-join algorithm make use of past connect choice so as to sort configuration,
-     * the exact algorithm still fluctuating as of 5/7/2014
-     *
-     */
-    public HashMap<String, Integer> connectChoices;
-
     /** @hide
      * Boost given to RSSI on a home network for the purpose of calculating the score
      * This adds stickiness to home networks, as defined by:
@@ -830,6 +812,16 @@
          */
         public static final long INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP = -1L;
 
+        /**
+         *  This constant indicates the current configuration has connect choice set
+         */
+        private static final int CONNECT_CHOICE_EXISTS = 1;
+
+        /**
+         *  This constant indicates the current configuration does not have connect choice set
+         */
+        private static final int CONNECT_CHOICE_NOT_EXISTS = -1;
+
         // fields for QualityNetwork Selection
         /**
          * Network selection status, should be in one of three status: enable, temporaily disabled
@@ -853,7 +845,128 @@
         private int[] mNetworkSeclectionDisableCounter = new int[NETWORK_SELECTION_DISABLED_MAX];
 
         /**
-         * return current Quality network selection status in String (for debug purpose)
+         * Connect Choice over this configuration
+         *
+         * When current wifi configuration is visible to the user but user explicitly choose to
+         * connect to another network X, the another networks X's configure key will be stored here.
+         * We will consider user has a preference of X over this network. And in the future,
+         * network selection will always give X a higher preference over this configuration.
+         * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP
+         */
+        private String mConnectChoice;
+
+        /**
+         * The system timestamp when we records the connectChoice. This value is obtained from
+         * System.currentTimeMillis
+         */
+        private long mConnectChoiceTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
+
+        /**
+         * Used to cache the temporary candidate during the network selection procedure. It will be
+         * kept updating once a new scan result has a higher score than current one
+         */
+        private ScanResult mCandidate;
+
+        /**
+         * Used to cache the score of the current temporary candidate during the network
+         * selection procedure.
+         */
+        private int mCandidateScore;
+
+        /**
+         * Indicate whether this network is visible in latest Qualified Network Selection. This
+         * means there is scan result found related to this Configuration and meet the minimum
+         * requirement. The saved network need not join latest Qualified Network Selection. For
+         * example, it is disabled. True means network is visible in latest Qualified Network
+         * Selection and false means network is invisible
+         */
+        private boolean mSeenInLastQualifiedNetworkSelection;
+
+        /**
+         * set whether this network is visible in latest Qualified Network Selection
+         * @param seen value set to candidate
+         */
+        public void setSeenInLastQualifiedNetworkSelection(boolean seen) {
+            mSeenInLastQualifiedNetworkSelection =  seen;
+        }
+
+        /**
+         * get whether this network is visible in latest Qualified Network Selection
+         * @return returns true -- network is visible in latest Qualified Network Selection
+         *         false -- network is invisible in latest Qualified Network Selection
+         */
+        public boolean getSeenInLastQualifiedNetworkSelection() {
+            return mSeenInLastQualifiedNetworkSelection;
+        }
+        /**
+         * set the temporary candidate of current network selection procedure
+         * @param scanCandidate {@link ScanResult} the candidate set to mCandidate
+         */
+        public void setCandidate(ScanResult scanCandidate) {
+            mCandidate = scanCandidate;
+        }
+
+        /**
+         * get the temporary candidate of current network selection procedure
+         * @return  returns {@link ScanResult} temporary candidate of current network selection
+         * procedure
+         */
+        public ScanResult getCandidate() {
+            return mCandidate;
+        }
+
+        /**
+         * set the score of the temporary candidate of current network selection procedure
+         * @param score value set to mCandidateScore
+         */
+        public void setCandidateScore(int score) {
+            mCandidateScore = score;
+        }
+
+        /**
+         * get the score of the temporary candidate of current network selection procedure
+         * @return returns score of the temporary candidate of current network selection procedure
+         */
+        public int getCandidateScore() {
+            return mCandidateScore;
+        }
+
+        /**
+         * get user preferred choice over this configuration
+         *@return returns configKey of user preferred choice over this configuration
+         */
+        public String getConnectChoice() {
+            return mConnectChoice;
+        }
+
+        /**
+         * set user preferred choice over this configuration
+         * @param newConnectChoice, the configKey of user preferred choice over this configuration
+         */
+        public void setConnectChoice(String newConnectChoice) {
+            mConnectChoice = newConnectChoice;
+        }
+
+        /**
+         * get the timeStamp when user select a choice over this configuration
+         * @return returns when current connectChoice is set (time from System.currentTimeMillis)
+         */
+        public long getConnectChoiceTimestamp() {
+            return mConnectChoiceTimestamp;
+        }
+
+        /**
+         * set the timeStamp when user select a choice over this configuration
+         * @param timeStamp, the timestamp set to connectChoiceTimestamp, expected timestamp should
+         *        be obtained from System.currentTimeMillis
+         */
+        public void setConnectChoiceTimestamp(long timeStamp) {
+            mConnectChoiceTimestamp = timeStamp;
+        }
+
+        /**
+         * get current Quality network selection status
+         * @return returns current Quality network selection status in String (for debug purpose)
          */
         public String getNetworkStatusString() {
             return QUALITY_NETWORK_SELECTION_STATUS[mStatus];
@@ -873,6 +986,7 @@
             }
         }
         /**
+         * get current network disable reason
          * @return current network disable reason in String (for debug purpose)
          */
         public String getNetworkDisableReasonString() {
@@ -881,6 +995,7 @@
 
         /**
          * get current network network selection status
+         * @return return current network network selection status
          */
         public int getNetworkSelectionStatus() {
             return mStatus;
@@ -900,12 +1015,14 @@
         }
 
         /**
-         * return whether current network is permanently disabled
+         * @return returns whether current network is permanently disabled
          */
         public boolean isNetworkPermanentlyDisabled() {
             return mStatus == NETWORK_SELECTION_PERMANENTLY_DISABLED;
         }
+
         /**
+         * set current networ work selection status
          * @param status network selection status to set
          */
         public void setNetworkSelectionStatus(int status) {
@@ -913,14 +1030,16 @@
                 mStatus = status;
             }
         }
+
         /**
-         * @return current network's disable reason
+         * @return returns current network's disable reason
          */
         public int getNetworkSelectionDisableReason() {
             return mNetworkSelectionDisableReason;
         }
 
         /**
+         * set Network disable reason
          * @param  reason Network disable reason
          */
         public void setNetworkSelectionDisableReason(int reason) {
@@ -930,12 +1049,17 @@
                 throw new IllegalArgumentException("Illegal reason value: " + reason);
             }
         }
+
         /**
-         * @param reason whether current network is disabled by this reason
+         * check whether network is disabled by this reason
+         * @param reason a specific disable reason
+         * @return true -- network is disabled for this reason
+         *         false -- network is not disabled for this reason
          */
         public boolean isDisabledByReason(int reason) {
             return mNetworkSelectionDisableReason == reason;
         }
+
         /**
          * @param timeStamp Set when current network is disabled in millisecond since January 1,
          * 1970 00:00:00.0 UTC
@@ -945,7 +1069,7 @@
         }
 
         /**
-         * @return Get when current network is disabled in millisecond since January 1,
+         * @return returns when current network is disabled in millisecond since January 1,
          * 1970 00:00:00.0 UTC
          */
         public long getDisableTime() {
@@ -953,6 +1077,7 @@
         }
 
         /**
+         * get the disable counter of a specific reason
          * @param  reason specific failure reason
          * @exception throw IllegalArgumentException for illegal input
          * @return counter number for specific error reason.
@@ -991,6 +1116,7 @@
                 throw new IllegalArgumentException("Illegal reason value: " + reason);
             }
         }
+
         /**
          * clear the counter of a specific failure reason
          * @hide
@@ -1004,6 +1130,7 @@
                 throw new IllegalArgumentException("Illegal reason value: " + reason);
             }
         }
+
         /**
          * clear all the failure reason counters
          */
@@ -1042,6 +1169,8 @@
             }
             mTemporarilyDisabledTimestamp = source.mTemporarilyDisabledTimestamp;
             mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
+            setConnectChoice(source.getConnectChoice());
+            setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
         }
 
         public void writeToParcel(Parcel dest) {
@@ -1053,6 +1182,13 @@
             }
             dest.writeLong(getDisableTime());
             dest.writeString(getNetworkSelectionBSSID());
+            if (getConnectChoice() != null) {
+                dest.writeInt(CONNECT_CHOICE_EXISTS);
+                dest.writeString(getConnectChoice());
+                dest.writeLong(getConnectChoiceTimestamp());
+            } else {
+                dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
+            }
         }
 
         public void readFromParcel(Parcel in) {
@@ -1064,6 +1200,13 @@
             }
             setDisableTime(in.readLong());
             setNetworkSelectionBSSID(in.readString());
+            if (in.readInt() == CONNECT_CHOICE_EXISTS) {
+                setConnectChoice(in.readString());
+                setConnectChoiceTimestamp(in.readLong());
+            } else {
+                setConnectChoice(null);
+                setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
+            }
         }
     }
 
@@ -1183,7 +1326,11 @@
                 }
             }
         }
-
+        if (mNetworkSelectionStatus.getConnectChoice() != null) {
+            sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice());
+            sbuf.append(" connect choice set time: ").append(mNetworkSelectionStatus
+                    .getConnectChoiceTimestamp());
+        }
 
         if (this.numAssociation > 0) {
             sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
@@ -1335,16 +1482,7 @@
                 sbuf.append('\n');
             }
         }
-        if (this.connectChoices != null) {
-            for(String key : this.connectChoices.keySet()) {
-                Integer choice = this.connectChoices.get(key);
-                if (choice != null) {
-                    sbuf.append(" choice: ").append(key);
-                    sbuf.append(" = ").append(choice);
-                    sbuf.append('\n');
-                }
-            }
-        }
+
         sbuf.append("triggeredLow: ").append(this.numUserTriggeredWifiDisableLowRSSI);
         sbuf.append(" triggeredBad: ").append(this.numUserTriggeredWifiDisableBadRSSI);
         sbuf.append(" triggeredNotHigh: ").append(this.numUserTriggeredWifiDisableNotHighRSSI);
@@ -1631,11 +1769,6 @@
 
             mIpConfiguration = new IpConfiguration(source.mIpConfiguration);
 
-            if ((source.connectChoices != null) && (source.connectChoices.size() > 0)) {
-                connectChoices = new HashMap<String, Integer>();
-                connectChoices.putAll(source.connectChoices);
-            }
-
             if ((source.linkedConfigurations != null)
                     && (source.linkedConfigurations.size() > 0)) {
                 linkedConfigurations = new HashMap<String, Integer>();
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 59b22bd..53efe6c 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -15,12 +15,14 @@
  */
 package android.net.wifi;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.security.Credentials;
 import android.text.TextUtils;
 
 import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
 import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
@@ -72,6 +74,13 @@
     public static final String KEYSTORE_URI = "keystore://";
 
     /**
+     * String representing the keystore URI used for wpa_supplicant,
+     * Unlike #KEYSTORE_URI, this supports a list of space-delimited aliases
+     * @hide
+     */
+    public static final String KEYSTORES_URI = "keystores://";
+
+    /**
      * String to set the engine value to when it should be enabled.
      * @hide
      */
@@ -103,6 +112,8 @@
     public static final String PLMN_KEY            = "plmn";
     /** @hide */
     public static final String PHASE1_KEY          = "phase1";
+    /** @hide */
+    public static final String CA_CERT_ALIAS_DELIMITER = " ";
 
     /** {@hide} */
     public static final String ENABLE_TLS_1_2 = "\"tls_disable_tlsv1_2=0\"";
@@ -113,7 +124,7 @@
     //By default, we enable TLS1.2. However, due to a known bug on some radius, we may disable it to
     // fall back to TLS 1.1.
     private boolean mTls12Enable =  true;
-    private X509Certificate mCaCert;
+    private X509Certificate[] mCaCerts;
     private PrivateKey mClientPrivateKey;
     private X509Certificate mClientCertificate;
 
@@ -145,7 +156,7 @@
             dest.writeString(entry.getValue());
         }
 
-        writeCertificate(dest, mCaCert);
+        writeCertificates(dest, mCaCerts);
 
         if (mClientPrivateKey != null) {
             String algorithm = mClientPrivateKey.getAlgorithm();
@@ -161,6 +172,17 @@
         dest.writeInt(mTls12Enable ? 1: 0);
     }
 
+    private void writeCertificates(Parcel dest, X509Certificate[] cert) {
+        if (cert != null && cert.length != 0) {
+            dest.writeInt(cert.length);
+            for (int i = 0; i < cert.length; i++) {
+                writeCertificate(dest, cert[i]);
+            }
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
     private void writeCertificate(Parcel dest, X509Certificate cert) {
         if (cert != null) {
             try {
@@ -186,7 +208,7 @@
                         enterpriseConfig.mFields.put(key, value);
                     }
 
-                    enterpriseConfig.mCaCert = readCertificate(in);
+                    enterpriseConfig.mCaCerts = readCertificates(in);
 
                     PrivateKey userKey = null;
                     int len = in.readInt();
@@ -210,6 +232,18 @@
                     return enterpriseConfig;
                 }
 
+                private X509Certificate[] readCertificates(Parcel in) {
+                    X509Certificate[] certs = null;
+                    int len = in.readInt();
+                    if (len > 0) {
+                        certs = new X509Certificate[len];
+                        for (int i = 0; i < len; i++) {
+                            certs[i] = readCertificate(in);
+                        }
+                    }
+                    return certs;
+                }
+
                 private X509Certificate readCertificate(Parcel in) {
                     X509Certificate cert = null;
                     int len = in.readInt();
@@ -430,6 +464,36 @@
     }
 
     /**
+     * Encode a CA certificate alias so it does not contain illegal character.
+     * @hide
+     */
+    public static String encodeCaCertificateAlias(String alias) {
+        byte[] bytes = alias.getBytes(StandardCharsets.UTF_8);
+        StringBuilder sb = new StringBuilder(bytes.length * 2);
+        for (byte o : bytes) {
+            sb.append(String.format("%02x", o & 0xFF));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Decode a previously-encoded CA certificate alias.
+     * @hide
+     */
+    public static String decodeCaCertificateAlias(String alias) {
+        byte[] data = new byte[alias.length() >> 1];
+        for (int n = 0, position = 0; n < alias.length(); n += 2, position++) {
+            data[position] = (byte) Integer.parseInt(alias.substring(n,  n + 2), 16);
+        }
+        try {
+            return new String(data, StandardCharsets.UTF_8);
+        } catch (NumberFormatException e) {
+            e.printStackTrace();
+            return alias;
+        }
+    }
+
+    /**
      * Set CA certificate alias.
      *
      * <p> See the {@link android.security.KeyChain} for details on installing or choosing
@@ -443,6 +507,35 @@
     }
 
     /**
+     * Set CA certificate aliases. When creating installing the corresponding certificate to
+     * the keystore, please use alias encoded by {@link #encodeCaCertificateAlias(String)}.
+     *
+     * <p> See the {@link android.security.KeyChain} for details on installing or choosing
+     * a certificate.
+     * </p>
+     * @param aliases identifies the certificate
+     * @hide
+     */
+    public void setCaCertificateAliases(@Nullable String[] aliases) {
+        if (aliases == null) {
+            setFieldValue(CA_CERT_KEY, null, CA_CERT_PREFIX);
+        } else if (aliases.length == 1) {
+            // Backwards compatibility: use the original cert prefix if setting only one alias.
+            setCaCertificateAlias(aliases[0]);
+        } else {
+            // Use KEYSTORES_URI which supports multiple aliases.
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < aliases.length; i++) {
+                if (i > 0) {
+                    sb.append(CA_CERT_ALIAS_DELIMITER);
+                }
+                sb.append(encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + aliases[i]));
+            }
+            setFieldValue(CA_CERT_KEY, sb.toString(), KEYSTORES_URI);
+        }
+    }
+
+    /**
      * Get CA certificate alias
      * @return alias to the CA certificate
      * @hide
@@ -452,6 +545,32 @@
     }
 
     /**
+     * Get CA certificate aliases
+     * @return alias to the CA certificate
+     * @hide
+     */
+    @Nullable public String[] getCaCertificateAliases() {
+        String value = getFieldValue(CA_CERT_KEY, "");
+        if (value.startsWith(CA_CERT_PREFIX)) {
+            // Backwards compatibility: parse the original alias prefix.
+            return new String[] {getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX)};
+        } else if (value.startsWith(KEYSTORES_URI)) {
+            String values = value.substring(KEYSTORES_URI.length());
+
+            String[] aliases = TextUtils.split(values, CA_CERT_ALIAS_DELIMITER);
+            for (int i = 0; i < aliases.length; i++) {
+                aliases[i] = decodeCaCertificateAlias(aliases[i]);
+                if (aliases[i].startsWith(Credentials.CA_CERTIFICATE)) {
+                    aliases[i] = aliases[i].substring(Credentials.CA_CERTIFICATE.length());
+                }
+            }
+            return aliases.length != 0 ? aliases : null;
+        } else {
+            return TextUtils.isEmpty(value) ? null : new String[] {value};
+        }
+    }
+
+    /**
      * Specify a X.509 certificate that identifies the server.
      *
      * <p>A default name is automatically assigned to the certificate and used
@@ -462,31 +581,76 @@
      * @param cert X.509 CA certificate
      * @throws IllegalArgumentException if not a CA certificate
      */
-    public void setCaCertificate(X509Certificate cert) {
+    public void setCaCertificate(@Nullable X509Certificate cert) {
         if (cert != null) {
             if (cert.getBasicConstraints() >= 0) {
-                mCaCert = cert;
+                mCaCerts = new X509Certificate[] {cert};
             } else {
                 throw new IllegalArgumentException("Not a CA certificate");
             }
         } else {
-            mCaCert = null;
+            mCaCerts = null;
         }
     }
 
     /**
-     * Get CA certificate
+     * Get CA certificate. If multiple CA certificates are configured previously,
+     * return the first one.
      * @return X.509 CA certificate
      */
-    public X509Certificate getCaCertificate() {
-        return mCaCert;
+    @Nullable public X509Certificate getCaCertificate() {
+        if (mCaCerts != null && mCaCerts.length > 0) {
+            return mCaCerts[0];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Specify a list of X.509 certificates that identifies the server. The validation
+     * passes if the CA of server certificate matches one of the given certificates.
+
+     * <p>Default names are automatically assigned to the certificates and used
+     * with this configuration. The framework takes care of installing the
+     * certificates when the config is saved and removing the certificates when
+     * the config is removed.
+     *
+     * @param certs X.509 CA certificates
+     * @throws IllegalArgumentException if any of the provided certificates is
+     *     not a CA certificate
+     */
+    public void setCaCertificates(@Nullable X509Certificate[] certs) {
+        if (certs != null) {
+            X509Certificate[] newCerts = new X509Certificate[certs.length];
+            for (int i = 0; i < certs.length; i++) {
+                if (certs[i].getBasicConstraints() >= 0) {
+                    newCerts[i] = certs[i];
+                } else {
+                    throw new IllegalArgumentException("Not a CA certificate");
+                }
+            }
+            mCaCerts = newCerts;
+        } else {
+            mCaCerts = null;
+        }
+    }
+
+    /**
+     * Get CA certificates.
+     */
+    @Nullable public X509Certificate[] getCaCertificates() {
+        if (mCaCerts != null || mCaCerts.length > 0) {
+            return mCaCerts;
+        } else {
+            return null;
+        }
     }
 
     /**
      * @hide
      */
     public void resetCaCertificate() {
-        mCaCert = null;
+        mCaCerts = null;
     }
 
     /** Set Client certificate alias.
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
index ff3d29f..ec9e462 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
@@ -43,7 +43,8 @@
     void publish(int sessionId, in PublishData publishData, in PublishSettings publishSettings);
     void subscribe(int sessionId, in SubscribeData subscribeData,
             in SubscribeSettings subscribeSettings);
-    void sendMessage(int sessionId, int peerId, in byte[] message, int messageLength);
+    void sendMessage(int sessionId, int peerId, in byte[] message, int messageLength,
+            int messageId);
     void stopSession(int sessionId);
     void destroySession(int sessionId);
 }
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
index 773f83b..50c34d9 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
@@ -32,7 +32,7 @@
     void onMatch(int peerId, in byte[] serviceSpecificInfo,
             int serviceSpecificInfoLength, in byte[] matchFilter, int matchFilterLength);
 
-    void onMessageSendSuccess();
-    void onMessageSendFail(int reason);
+    void onMessageSendSuccess(int messageId);
+    void onMessageSendFail(int messageId, int reason);
     void onMessageReceived(int peerId, in byte[] message, int messageLength);
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index 877f993..cb82268 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -319,13 +319,14 @@
     /**
      * {@hide}
      */
-    public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength) {
+    public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength,
+            int messageId) {
         try {
             if (VDBG) {
                 Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId
-                        + ", messageLength=" + messageLength);
+                        + ", messageLength=" + messageLength + ", messageId=" + messageId);
             }
-            mService.sendMessage(sessionId, peerId, message, messageLength);
+            mService.sendMessage(sessionId, peerId, message, messageLength, messageId);
         } catch (RemoteException e) {
             Log.w(TAG, "subscribe RemoteException (FYI - ignoring): " + e);
         }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
index c6b384e..d0a9410 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -103,8 +103,11 @@
      * @param message The message to be transmitted.
      * @param messageLength The number of bytes from the {@code message} to be
      *            transmitted.
+     * @param messageId An arbitrary integer used by the caller to identify the
+     *            message. The same integer ID will be returned in the callbacks
+     *            indicated message send success or failure.
      */
-    public void sendMessage(int peerId, byte[] message, int messageLength) {
-        mManager.sendMessage(mSessionId, peerId, message, messageLength);
+    public void sendMessage(int peerId, byte[] message, int messageLength, int messageId) {
+        mManager.sendMessage(mSessionId, peerId, message, messageLength, messageId);
     }
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
index c9d08c7..d5e59f0 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
@@ -210,10 +210,10 @@
                                 msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2), msg.arg2);
                         break;
                     case LISTEN_MESSAGE_SEND_SUCCESS:
-                        WifiNanSessionListener.this.onMessageSendSuccess();
+                        WifiNanSessionListener.this.onMessageSendSuccess(msg.arg1);
                         break;
                     case LISTEN_MESSAGE_SEND_FAIL:
-                        WifiNanSessionListener.this.onMessageSendFail(msg.arg1);
+                        WifiNanSessionListener.this.onMessageSendFail(msg.arg1, msg.arg2);
                         break;
                     case LISTEN_MESSAGE_RECEIVED:
                         WifiNanSessionListener.this.onMessageReceived(msg.arg2,
@@ -306,7 +306,7 @@
      * {@link WifiNanSessionListener#onMessageSendFail(int)} will be received -
      * never both.
      */
-    public void onMessageSendSuccess() {
+    public void onMessageSendSuccess(int messageId) {
         if (VDBG) Log.v(TAG, "onMessageSendSuccess: called in stub - override if interested");
     }
 
@@ -325,7 +325,7 @@
      * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
      *            codes.
      */
-    public void onMessageSendFail(int reason) {
+    public void onMessageSendFail(int messageId, int reason) {
         if (VDBG) Log.v(TAG, "onMessageSendFail: called in stub - override if interested");
     }
 
@@ -401,19 +401,21 @@
         }
 
         @Override
-        public void onMessageSendSuccess() {
+        public void onMessageSendSuccess(int messageId) {
             if (VDBG) Log.v(TAG, "onMessageSendSuccess");
 
             Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_SUCCESS);
+            msg.arg1 = messageId;
             mHandler.sendMessage(msg);
         }
 
         @Override
-        public void onMessageSendFail(int reason) {
+        public void onMessageSendFail(int messageId, int reason) {
             if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason);
 
             Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_FAIL);
-            msg.arg1 = reason;
+            msg.arg1 = messageId;
+            msg.arg2 = reason;
             mHandler.sendMessage(msg);
         }