Merge "Import translations. DO NOT MERGE"
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 96522f7..b01b0a8 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -297,6 +297,7 @@
         MediametricsMediadrmReported mediametrics_mediadrm_reported = 198;
         MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199;
         MediametricsRecorderReported mediametrics_recorder_reported = 200;
+        MediametricsDrmManagerReported mediametrics_drmmanager_reported = 201;
         CarPowerStateChanged car_power_state_changed = 203;
         GarageModeInfo garage_mode_info = 204;
         TestAtomReported test_atom_reported = 205 [(log_from_module) = "cts"];
@@ -6204,6 +6205,41 @@
 }
 
 /**
+ * Track Legacy DRM usage
+ * Logged from
+ *   frameworks/av/drm/drmserver/DrmManager.cpp
+ */
+message MediametricsDrmManagerReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    enum Method {
+        METHOD_NOT_FOUND       = -1;
+        GET_CONSTRAINTS        =  0;
+        GET_METADATA           =  1;
+        CAN_HANDLE             =  2;
+        PROCESS_DRM_INFO       =  3;
+        ACQUIRE_DRM_INFO       =  4;
+        SAVE_RIGHTS            =  5;
+        GET_ORIGINAL_MIME_TYPE =  6;
+        GET_DRM_OBJECT_TYPE    =  7;
+        CHECK_RIGHTS_STATUS    =  8;
+        REMOVE_RIGHTS          =  9;
+        REMOVE_ALL_RIGHTS      = 10;
+        OPEN_CONVERT_SESSION   = 11;
+        OPEN_DECRYPT_SESSION   = 12;
+    }
+
+    // plugin_id+description inform which Legacy DRM plugins are still in use on device
+    optional string plugin_id = 5;
+    optional string description = 6;
+    optional Method method = 7;
+    optional string mime_types = 8;
+}
+
+/**
  * State of a dangerous permission requested by a package
  */
 message DangerousPermissionState {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 33e7540..f243199 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -97,14 +97,17 @@
      * @return the newly created CachedBluetoothDevice object
      */
     public CachedBluetoothDevice addDevice(BluetoothDevice device) {
-        LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
-        CachedBluetoothDevice newDevice = new CachedBluetoothDevice(mContext, profileManager,
-                device);
-        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice);
+        CachedBluetoothDevice newDevice;
+        final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
         synchronized (this) {
-            if (!mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
-                mCachedDevices.add(newDevice);
-                mBtManager.getEventManager().dispatchDeviceAdded(newDevice);
+            newDevice = findDevice(device);
+            if (newDevice == null) {
+                newDevice = new CachedBluetoothDevice(mContext, profileManager, device);
+                mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice);
+                if (!mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
+                    mCachedDevices.add(newDevice);
+                    mBtManager.getEventManager().dispatchDeviceAdded(newDevice);
+                }
             }
         }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index dc47de8..806f22f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -191,6 +191,25 @@
     }
 
     /**
+     * Test to verify addDevice(), the duplicated device should not added.
+     */
+    @Test
+    public void addDevice_addDuplicatedDevice_duplicateDeviceShouldNotAdded() {
+        final CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+        final CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+        assertThat(cachedDevice2).isNotNull();
+        final CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice2);
+        assertThat(cachedDevice3).isNotNull();
+        final CachedBluetoothDevice cachedDevice4 = mCachedDeviceManager.addDevice(mDevice2);
+        assertThat(cachedDevice4).isNotNull();
+
+        final Collection<CachedBluetoothDevice> devices =
+                mCachedDeviceManager.getCachedDevicesCopy();
+        assertThat(devices.size()).isEqualTo(2);
+    }
+
+    /**
      * Test to verify findDevice(), new device has the same HiSyncId.
      */
     @Test
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 9095b5d..557b7c9 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.hotspot2;
 
+import android.annotation.Nullable;
 import android.net.wifi.hotspot2.pps.Credential;
 import android.net.wifi.hotspot2.pps.HomeSp;
 import android.net.wifi.hotspot2.pps.Policy;
@@ -78,6 +79,29 @@
     public HomeSp getHomeSp() { return mHomeSp; }
 
     /**
+     * Configurations under AAAServerTrustedNames subtree.
+     */
+    private String[] mAaaServerTrustedNames = null;
+    /**
+     * Set the AAA server trusted names information.
+     *
+     * @param aaaServerTrustedNames The AAA server trusted names information to set to
+     * @hide
+     */
+    public void setAaaServerTrustedNames(@Nullable String[] aaaServerTrustedNames) {
+        mAaaServerTrustedNames = aaaServerTrustedNames;
+    }
+    /**
+     * Get the AAA server trusted names information.
+     *
+     * @return AAA server trusted names information
+     * @hide
+     */
+    public @Nullable String[] getAaaServerTrustedNames() {
+        return mAaaServerTrustedNames;
+    }
+
+    /**
      * Configurations under Credential subtree.
      */
     private Credential mCredential = null;
@@ -409,6 +433,7 @@
         mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes;
         mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes;
         mServiceFriendlyNames = source.mServiceFriendlyNames;
+        mAaaServerTrustedNames = source.mAaaServerTrustedNames;
     }
 
     @Override
@@ -432,6 +457,7 @@
         dest.writeLong(mUsageLimitStartTimeInMillis);
         dest.writeLong(mUsageLimitDataLimit);
         dest.writeLong(mUsageLimitTimeLimitInMinutes);
+        dest.writeStringArray(mAaaServerTrustedNames);
         Bundle bundle = new Bundle();
         bundle.putSerializable("serviceFriendlyNames",
                 (HashMap<String, String>) mServiceFriendlyNames);
@@ -448,6 +474,8 @@
         }
         PasspointConfiguration that = (PasspointConfiguration) thatObject;
         return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp))
+                && (mAaaServerTrustedNames == null ? that.mAaaServerTrustedNames == null
+                : Arrays.equals(mAaaServerTrustedNames, that.mAaaServerTrustedNames))
                 && (mCredential == null ? that.mCredential == null
                 : mCredential.equals(that.mCredential))
                 && (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy))
@@ -517,6 +545,10 @@
             builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet())
                     .append("\n");
         }
+        if (mAaaServerTrustedNames != null) {
+            builder.append("AAAServerTrustedNames: ")
+                    .append(String.join(";", mAaaServerTrustedNames)).append("\n");
+        }
         if (mServiceFriendlyNames != null) {
             builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames);
         }
@@ -619,6 +651,7 @@
                 config.setUsageLimitStartTimeInMillis(in.readLong());
                 config.setUsageLimitDataLimit(in.readLong());
                 config.setUsageLimitTimeLimitInMinutes(in.readLong());
+                config.setAaaServerTrustedNames(in.createStringArray());
                 Bundle bundle = in.readBundle();
                 Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
                         "serviceFriendlyNames");
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
index 984cf7d..68358ab 100644
--- a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
@@ -25,6 +25,8 @@
 import android.util.Log;
 import android.util.Pair;
 
+import org.xml.sax.SAXException;
+
 import java.io.IOException;
 import java.text.DateFormat;
 import java.text.ParseException;
@@ -36,8 +38,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.xml.sax.SAXException;
-
 /**
  * Utility class for converting OMA-DM (Open Mobile Alliance's Device Management)
  * PPS-MO (PerProviderSubscription Management Object) XML tree to a
@@ -147,6 +147,47 @@
     private static final String NODE_EXTENSION = "Extension";
 
     /**
+     * Fields under Extension/Android subtree.
+     */
+    /*
+     * This node is used to put Android specific extension nodes and must be put
+     * under "Extension" node. Nodes with unknown names are allowed under this subtree.
+     * If there is any new node added in later release, it won't break older release parsing.
+     * <p>
+     * Ex:
+     * <Node>
+     *   <NodeName>Extension</NodeName>
+     *   <Node>
+     *     <NodeName>Android</NodeName>
+     *     <Node>
+     *       <NodeName>AndroidSpecificAttribute</NodeName>
+     *       <Value>AndroidSpecificValue</Value>
+     *     </Node>
+     *     <Node>
+     *       <NodeName>AndroidSpecificAttribute2</NodeName>
+     *       <Value>AndroidSpecificValue2</Value>
+     *     </Node>
+     *   </Node>
+     * </Node>
+     */
+    private static final String NODE_VENDOR_ANDROID = "Android";
+    /*
+     * This node describes AAA server trusted names. The trusted name must be put in
+     * a leaf named "FQDN". More than one trusted names can be provided by using
+     * semicolons to separate the strings (e.g., example.org;example.com).
+     * <p>
+     * Ex:
+     * <Node>
+     *   <NodeName>AAAServerTrustedNames</NodeName>
+     *   <Node>
+     *     <NodeName>FQDN</NodeName>
+     *     <Value>trusted.com;auth.net</Value>
+     *  </Node>
+     * <Node>
+     */
+    private static final String NODE_AAA_SERVER_TRUSTED_NAMES = "AAAServerTrustedNames";
+
+    /**
      * Fields under HomeSP subtree.
      */
     private static final String NODE_HOMESP = "HomeSP";
@@ -633,7 +674,7 @@
                     break;
                 case NODE_EXTENSION:
                     // All vendor specific information will be under this node.
-                    Log.d(TAG, "Ignore Extension node for vendor specific information");
+                    parseExtension(child, config);
                     break;
                 default:
                     throw new ParsingException("Unknown node: " + child.getName());
@@ -1572,6 +1613,92 @@
     }
 
     /**
+     * Parse configurations under PerProviderSubscription/Extension/Android/AAAServerTrustedNames
+     * subtree.
+     *
+     * @param node PPSNode representing the root of the
+     *             PerProviderSubscription/Extension/Android/AAAServerTrustedNames subtree
+     * @return String[] list of trusted name
+     * @throws ParsingException
+     */
+    private static String[] parseAaaServerTrustedNames(PPSNode node) throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for AAAServerTrustedNames instance");
+        }
+        String fqdnListStr = null;
+        String[] fqdnListArray = null;
+        for (PPSNode child : node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_FQDN:
+                    fqdnListStr = getPpsNodeValue(child);
+                    fqdnListArray = fqdnListStr.split(";");
+                    break;
+                default:
+                    throw new ParsingException(
+                            "Unknown node under AAAServerTrustedNames instance: "
+                            + child.getName());
+            }
+        }
+        if (fqdnListArray == null) {
+            throw new ParsingException("AAAServerTrustedNames instance missing FQDN field");
+        }
+
+        return fqdnListArray;
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/Extension/Android subtree.
+     *
+     * @param node PPSNode representing the root of PerProviderSubscription/Extension
+     *             subtree
+     * @param config Instance of {@link PasspointConfiguration}
+     * @throws ParsingException
+     */
+    private static void parseVendorAndroidExtension(PPSNode node, PasspointConfiguration config)
+            throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for Extension");
+        }
+        for (PPSNode child : node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_AAA_SERVER_TRUSTED_NAMES:
+                    config.setAaaServerTrustedNames(parseAaaServerTrustedNames(child));
+                    break;
+                default:
+                    // Don't raise an exception for unknown nodes to avoid breaking old release
+                    Log.w(TAG, "Unknown node under Android Extension: " + child.getName());
+            }
+        }
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/Extension subtree.
+     *
+     * @param node PPSNode representing the root of PerProviderSubscription/Extension
+     *             subtree
+     * @param config Instance of {@link PasspointConfiguration}
+     * @throws ParsingException
+     */
+    private static void parseExtension(PPSNode node, PasspointConfiguration config)
+            throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for Extension");
+        }
+        for (PPSNode child : node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_VENDOR_ANDROID:
+                    parseVendorAndroidExtension(child, config);
+                    break;
+                default:
+                    // Unknown nodes under Extension won't raise exception.
+                    // This allows adding new nodes in the future and
+                    // won't break older release.
+                    Log.w(TAG, "Unknown node under Extension: " + child.getName());
+            }
+        }
+    }
+
+    /**
      * Convert a hex string to a byte array.
      *
      * @param str String containing hex values
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 9409c03..b037703 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -1135,6 +1135,7 @@
             Log.d(TAG, "Missing CA Certificate for user credential");
             return false;
         }
+
         return true;
     }
 
diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml
index e4472ce..e9afb35 100644
--- a/wifi/tests/assets/pps/PerProviderSubscription.xml
+++ b/wifi/tests/assets/pps/PerProviderSubscription.xml
@@ -19,6 +19,30 @@
           <NodeName>VendorSpecific</NodeName>
           <Value>Test</Value>
         </Node>
+        <Node>
+          <NodeName>VendorExtension</NodeName>
+          <Node>
+            <NodeName>VendorAttribute</NodeName>
+            <Value>VendorValue</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>Android</NodeName>
+          <Node>
+            <NodeName>AAAServerTrustedNames</NodeName>
+            <Node>
+              <NodeName>FQDN</NodeName>
+              <Value>trusted.fqdn.com;another-trusted.fqdn.com</Value>
+            </Node>
+          </Node>
+          <Node>
+            <NodeName>NewSubTree</NodeName>
+            <Node>
+              <NodeName>NewAttribute</NodeName>
+              <Value>NewValue</Value>
+            </Node>
+          </Node>
+        </Node>
       </Node>
       <Node>
         <NodeName>HomeSP</NodeName>
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index fc03e7e..88740d8 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -149,6 +149,8 @@
         PasspointConfiguration config = new PasspointConfiguration();
         config.setUpdateIdentifier(1234);
         config.setHomeSp(createHomeSp());
+        config.setAaaServerTrustedNames(
+                new String[] {"trusted.fqdn.com", "another-trusted.fqdn.com"});
         config.setCredential(createCredential());
         config.setPolicy(createPolicy());
         config.setSubscriptionUpdate(createSubscriptionUpdate());
@@ -284,6 +286,19 @@
     }
 
     /**
+     * Verify parcel read/write for a configuration that doesn't contain AAA server trusted names
+     * list.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithoutAaaServerTrustedNames() throws Exception {
+        PasspointConfiguration config = createConfig();
+        config.setAaaServerTrustedNames(null);
+        verifyParcel(config);
+    }
+
+    /**
      * Verify that a default/empty configuration is invalid.
      *
      * @throws Exception
@@ -383,6 +398,21 @@
     }
 
     /**
+     * Verify that a configuration without AAA server trusted names is valid for R1 and R2,
+     * since AAA server trusted names are optional for R1 and R2.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateConfigWithoutAaaServerTrustedNames() throws Exception {
+        PasspointConfiguration config = createConfig();
+        config.setAaaServerTrustedNames(null);
+
+        assertTrue(config.validate());
+        assertTrue(config.validateForR2());
+    }
+
+    /**
      * Verify that a configuration with a trust root certificate URL exceeding the max size
      * is invalid.
      *
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
index 66c595f..1ac9cb8 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
@@ -135,6 +135,9 @@
         homeSp.setOtherHomePartners(new String[] {"other.fqdn.com"});
         config.setHomeSp(homeSp);
 
+        config.setAaaServerTrustedNames(
+                new String[] {"trusted.fqdn.com", "another-trusted.fqdn.com"});
+
         // Credential configuration.
         Credential credential = new Credential();
         credential.setCreationTimeInMillis(format.parse("2016-01-01T10:00:00Z").getTime());