Merge "Fix invalid padding bits for 7-bit ASCII encoding"
diff --git a/Android.bp b/Android.bp
index 1d5e858..7c34ea0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -185,6 +185,7 @@
 
     // AIDL sources from external directories
     ":dumpstate_aidl",
+    ":framework_native_aidl",
     ":gatekeeper_aidl",
     ":gsiservice_aidl",
     ":incidentcompanion_aidl",
@@ -237,12 +238,6 @@
     srcs: framework_srcs,
 
     aidl: {
-        // TODO(b/70046217) remove this by moving the AIDL files into frameworks/base
-        // so that they are referenced via framework-core-sources
-        include_dirs: [
-            "frameworks/native/aidl/binder",
-            "frameworks/native/aidl/gui",
-        ],
         generate_get_transaction_name: true,
     },
 
@@ -849,6 +844,11 @@
     create_stubs: false,
 }
 
+doc_defaults {
+    name: "framework-dokka-docs-default",
+    create_stubs: false,
+}
+
 stubs_defaults {
     name: "metalava-api-stubs-default",
     srcs: [
@@ -1020,7 +1020,7 @@
 }
 
 droiddoc {
-    name: "ds-docs",
+    name: "ds-docs-java",
     defaults: ["framework-docs-default"],
     srcs: [
         ":framework-doc-stubs",
@@ -1049,6 +1049,58 @@
 }
 
 droiddoc {
+    name: "ds-docs-kt",
+    defaults: ["framework-dokka-docs-default"],
+    srcs: [
+        ":framework-doc-stubs",
+    ],
+    args: "-noJdkLink -links https://kotlinlang.org/api/latest/jvm/stdlib/^external/dokka/package-list " +
+    "-noStdlibLink",
+    proofread_file: "ds-dokka-proofread.txt",
+    dokka_enabled: true,
+}
+
+java_genrule {
+    name: "ds-docs",
+    tools: [
+        "zip2zip",
+        "merge_zips",
+    ],
+    srcs: [
+        ":ds-docs-java{.docs.zip}",
+        ":ds-docs-kt{.docs.zip}",
+    ],
+    out: ["ds-docs.zip"],
+    dist: {
+        targets: ["docs"],
+    },
+    cmd: "$(location zip2zip) -i $(location :ds-docs-kt{.docs.zip}) -o $(genDir)/ds-docs-kt-moved.zip **/*:en/reference/kotlin && " +
+         "$(location merge_zips) $(out) $(location :ds-docs-java{.docs.zip}) $(genDir)/ds-docs-kt-moved.zip",
+}
+
+java_genrule {
+    name: "ds-docs-switched",
+    tools: [
+        "switcher4",
+        "soong_zip",
+    ],
+    srcs: [
+        ":ds-docs-java{.docs.zip}",
+        ":ds-docs-kt{.docs.zip}",
+    ],
+    out: ["ds-docs-switched.zip"],
+    dist: {
+        targets: ["docs"],
+    },
+    cmd: "unzip $(location :ds-docs-java{.docs.zip}) -d $(genDir) && " +
+         "unzip $(location :ds-docs-kt{.docs.zip}) -d $(genDir)/en/reference/kotlin && " +
+         "SWITCHER=$$(cd $$(dirname $(location switcher4)) && pwd)/$$(basename $(location switcher4)) && " +
+         "(cd $(genDir)/en/reference && $$SWITCHER --work platform) && " +
+         "$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)",
+}
+
+
+droiddoc {
     name: "ds-static-docs",
     defaults: ["framework-docs-default"],
     srcs: [
diff --git a/api/current.txt b/api/current.txt
index 0e45300..70f3400 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -43080,6 +43080,7 @@
     method public boolean canChangeDtmfToneLength();
     method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot();
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
     method public int getCallState();
     method public int getCardIdForDefaultEuicc();
diff --git a/api/system-current.txt b/api/system-current.txt
index 9200ce5..bbb6e24 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6353,7 +6353,6 @@
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRebootRequiredForModemConfigChange();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index fb78789..cfb363a08 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -118,6 +118,8 @@
      */
     public static final int PAN_OPERATION_SUCCESS = 1004;
 
+    private final Context mContext;
+
     private BluetoothAdapter mAdapter;
     private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector =
             new BluetoothProfileConnector(this, BluetoothProfile.PAN,
@@ -136,6 +138,7 @@
     @UnsupportedAppUsage
     /*package*/ BluetoothPan(Context context, ServiceListener listener) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mContext = context;
         mProfileConnector.connect(context, listener);
     }
 
@@ -287,11 +290,12 @@
 
     @UnsupportedAppUsage
     public void setBluetoothTethering(boolean value) {
-        if (DBG) log("setBluetoothTethering(" + value + ")");
+        String pkgName = mContext.getOpPackageName();
+        if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
         final IBluetoothPan service = getService();
         if (service != null && isEnabled()) {
             try {
-                service.setBluetoothTethering(value);
+                service.setBluetoothTethering(value, pkgName);
             } catch (RemoteException e) {
                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             }
diff --git a/core/java/android/content/pm/PackageList.java b/core/java/android/content/pm/PackageList.java
index f781758..e3eb2c5 100644
--- a/core/java/android/content/pm/PackageList.java
+++ b/core/java/android/content/pm/PackageList.java
@@ -52,6 +52,13 @@
     }
 
     @Override
+    public void onPackageChanged(String packageName, int uid) {
+        if (mWrappedObserver != null) {
+            mWrappedObserver.onPackageChanged(packageName, uid);
+        }
+    }
+
+    @Override
     public void onPackageRemoved(String packageName, int uid) {
         if (mWrappedObserver != null) {
             mWrappedObserver.onPackageRemoved(packageName, uid);
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index c299369..0694c5f 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -63,6 +63,8 @@
     public interface PackageListObserver {
         /** A package was added to the system. */
         void onPackageAdded(@NonNull String packageName, int uid);
+        /** A package was changed - either installed for a specific user or updated. */
+        default void onPackageChanged(@NonNull String packageName, int uid) {}
         /** A package was removed from the system. */
         void onPackageRemoved(@NonNull String packageName, int uid);
     }
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index ab0a0ef..a839ec1 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -114,7 +114,7 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public ApduServiceInfo(ResolveInfo info, String description,
+    public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
             ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
             boolean requiresUnlock, int bannerResource, int uid,
             String settingsActivityName, String offHost, String staticOffHost) {
@@ -124,7 +124,7 @@
         this.mDynamicAidGroups = new HashMap<String, AidGroup>();
         this.mOffHostName = offHost;
         this.mStaticOffHostName = staticOffHost;
-        this.mOnHost = (offHost == null);
+        this.mOnHost = onHost;
         this.mRequiresDeviceUnlock = requiresUnlock;
         for (AidGroup aidGroup : staticAidGroups) {
             this.mStaticAidGroups.put(aidGroup.category, aidGroup);
@@ -570,7 +570,7 @@
             int bannerResource = source.readInt();
             int uid = source.readInt();
             String settingsActivityName = source.readString();
-            return new ApduServiceInfo(info, description, staticAidGroups,
+            return new ApduServiceInfo(info, onHost, description, staticAidGroups,
                     dynamicAidGroups, requiresUnlock, bannerResource, uid,
                     settingsActivityName, offHostName, staticOffHostName);
         }
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 1351380..9b8a40a 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -150,6 +150,14 @@
     void startTethering(in String[] dhcpRanges);
 
     /**
+     * Start tethering services with the specified dhcp server range and
+     * DNS proxy config.
+     * {@code boolean} is used to control legacy DNS proxy server.
+     * {@code String[]} is a set of start end pairs defining the ranges.
+     */
+    void startTetheringWithConfiguration(boolean usingLegacyDnsProxy, in String[] dhcpRanges);
+
+    /**
      * Stop currently running tethering services
      */
     @UnsupportedAppUsage
diff --git a/core/java/android/view/textclassifier/OWNERS b/core/java/android/view/textclassifier/OWNERS
new file mode 100644
index 0000000..893a083
--- /dev/null
+++ b/core/java/android/view/textclassifier/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 709498
+
+toki@google.com
+tonymak@google.com
+zilka@google.com
+jalt@google.com
+joannechung@google.com
+svetoslavganov@google.com
+eugeniom@google.com
+samsellem@google.com
\ No newline at end of file
diff --git a/core/res/res/values-mcc450-mnc08/config.xml b/core/res/res/values-mcc450-mnc08/config.xml
index ca26ec1..5edbaed 100644
--- a/core/res/res/values-mcc450-mnc08/config.xml
+++ b/core/res/res/values-mcc450-mnc08/config.xml
@@ -28,4 +28,14 @@
     <!-- Do not set the system language as value of EF LI/EF PL -->
     <bool name="config_use_sim_language_file">false</bool>
 
+    <!-- Configures encoding type to parse the User Data of an SMS for reserved TP-DCS value.
+         Refer to SmsConstants.java
+         ENCODING_UNKNOWN = 0;
+         ENCODING_7BIT = 1;
+         ENCODING_8BIT = 2;
+         ENCODING_16BIT = 3;
+         ENCODING_KSC5601 = 4;
+         -->
+    <integer name="default_reserved_data_coding_scheme">4</integer>
+
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 111f937..3c62bda 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2824,6 +2824,16 @@
         string that's stored in 8-bit unpacked format) characters.-->
     <bool translatable="false" name="config_sms_decode_gsm_8bit_data">false</bool>
 
+    <!-- Configures encoding type to parse the User Data of an SMS for reserved TP-DCS value.
+         Refer to SmsConstants.java
+         ENCODING_UNKNOWN = 0;
+         ENCODING_7BIT = 1;
+         ENCODING_8BIT = 2;
+         ENCODING_16BIT = 3;
+         ENCODING_KSC5601 = 4;
+         -->
+    <integer name="default_reserved_data_coding_scheme">2</integer>
+
     <!-- If EMS is not supported, framework breaks down EMS into single segment SMS
          and adds page info " x/y". This config is used to set which carrier doesn't
          support EMS and whether page info should be added at the beginning or the end.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 00808df..02154dd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2498,6 +2498,7 @@
   <java-symbol type="attr" name="ambientShadowAlpha" />
   <java-symbol type="attr" name="spotShadowAlpha" />
   <java-symbol type="bool" name="config_sms_decode_gsm_8bit_data" />
+  <java-symbol type="integer" name="default_reserved_data_coding_scheme" />
   <java-symbol type="dimen" name="text_size_small_material" />
   <java-symbol type="attr" name="checkMarkGravity" />
   <java-symbol type="layout" name="select_dialog_singlechoice_material" />
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4d8d992..5c9921a 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -51,7 +51,7 @@
         "android.hardware.contexthub-V1.0-java",
         "android.hidl.manager-V1.2-java",
         "dnsresolver_aidl_interface-V2-java",
-        "netd_aidl_interface-V2-java",
+        "netd_aidl_interface-java",
         "netd_event_listener_interface-java",
     ],
 }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index e29bf8d..7f19f06 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1003,11 +1003,15 @@
 
     @Override
     public void startTethering(String[] dhcpRange) {
+        startTetheringWithConfiguration(true, dhcpRange);
+    }
+
+    @Override
+    public void startTetheringWithConfiguration(boolean usingLegacyDnsProxy, String[] dhcpRange) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         // an odd number of addrs will fail
-
         try {
-            mNetdService.tetherStart(dhcpRange);
+            mNetdService.tetherStartWithConfiguration(usingLegacyDnsProxy, dhcpRange);
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index fbe2589..29c4bad 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -130,6 +130,11 @@
         }
 
         @Override
+        public void onPackageChanged(@NonNull String packageName, int uid) {
+            sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+        }
+
+        @Override
         public void onPackageRemoved(String packageName, int uid) {
             sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
         }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 86d1212..5fd5c4b 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -455,7 +455,20 @@
 
             @Override
             public void onServiceConnected(int profile, BluetoothProfile proxy) {
-                ((BluetoothPan) proxy).setBluetoothTethering(enable);
+                // Clear identify is fine because caller already pass tethering permission at
+                // ConnectivityService#startTethering()(or stopTethering) before the control comes
+                // here. Bluetooth will check tethering permission again that there is
+                // Context#getOpPackageName() under BluetoothPan#setBluetoothTethering() to get
+                // caller's package name for permission check.
+                // Calling BluetoothPan#setBluetoothTethering() here means the package name always
+                // be system server. If calling identity is not cleared, that package's uid might
+                // not match calling uid and end up in permission denied.
+                final long identityToken = Binder.clearCallingIdentity();
+                try {
+                    ((BluetoothPan) proxy).setBluetoothTethering(enable);
+                } finally {
+                    Binder.restoreCallingIdentity(identityToken);
+                }
                 // TODO: Enabling bluetooth tethering can fail asynchronously here.
                 // We should figure out a way to bubble up that failure instead of sending success.
                 final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e7a8b13..1bd29e5 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -58,7 +58,6 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkMisc;
-import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.net.UidRange;
 import android.net.VpnService;
@@ -114,7 +113,6 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -902,38 +900,6 @@
     }
 
     /**
-     * Analyzes the passed LinkedProperties to figure out whether it routes to most of the IP space.
-     *
-     * This returns true if the passed LinkedProperties contains routes to either most of the IPv4
-     * space or to most of the IPv6 address space, where "most" is defined by the value of the
-     * MOST_IPV{4,6}_ADDRESSES_COUNT constants : if more than this number of addresses are matched
-     * by any of the routes, then it's decided that most of the space is routed.
-     * @hide
-     */
-    @VisibleForTesting
-    static boolean providesRoutesToMostDestinations(LinkProperties lp) {
-        final List<RouteInfo> routes = lp.getAllRoutes();
-        if (routes.size() > MAX_ROUTES_TO_EVALUATE) return true;
-        final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator();
-        TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator);
-        TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator);
-        for (final RouteInfo route : routes) {
-            if (route.getType() == RouteInfo.RTN_UNREACHABLE) continue;
-            IpPrefix destination = route.getDestination();
-            if (destination.isIPv4()) {
-                ipv4Prefixes.add(destination);
-            } else {
-                ipv6Prefixes.add(destination);
-            }
-        }
-        if (NetworkUtils.routedIPv4AddressCount(ipv4Prefixes) > MOST_IPV4_ADDRESSES_COUNT) {
-            return true;
-        }
-        return NetworkUtils.routedIPv6AddressCount(ipv6Prefixes)
-                .compareTo(MOST_IPV6_ADDRESSES_COUNT) >= 0;
-    }
-
-    /**
      * Attempt to perform a seamless handover of VPNs by only updating LinkProperties without
      * registering a new NetworkAgent. This is not always possible if the new VPN configuration
      * has certain changes, in which case this method would just return {@code false}.
@@ -1079,7 +1045,8 @@
             // TEMP use the old jni calls until there is support for netd address setting
             StringBuilder builder = new StringBuilder();
             for (LinkAddress address : config.addresses) {
-                builder.append(" " + address);
+                builder.append(" ");
+                builder.append(address);
             }
             if (jniSetAddresses(interfaze, builder.toString()) < 1) {
                 throw new IllegalArgumentException("At least one address must be specified");
@@ -1163,7 +1130,7 @@
 
     // Note: Return type guarantees results are deduped and sorted, which callers require.
     private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
-        SortedSet<Integer> uids = new TreeSet<Integer>();
+        SortedSet<Integer> uids = new TreeSet<>();
         for (String app : packageNames) {
             int uid = getAppUid(app, userHandle);
             if (uid != -1) uids.add(uid);
@@ -1266,7 +1233,7 @@
         // UidRange#createForUser returns the entire range of UIDs available to a macro-user.
         // This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE}
         final UidRange userRange = UidRange.createForUser(userHandle);
-        final List<UidRange> ranges = new ArrayList<UidRange>();
+        final List<UidRange> ranges = new ArrayList<>();
         for (UidRange range : existingRanges) {
             if (userRange.containsRange(range)) {
                 ranges.add(range);
@@ -1765,7 +1732,7 @@
             byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
             serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
         }
-        if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
+        if (userCert == null || caCert == null || serverCert == null) {
             throw new IllegalStateException("Cannot load credentials");
         }
 
@@ -1884,7 +1851,7 @@
      * Return the information of the current ongoing legacy VPN.
      * Callers are responsible for checking permissions if needed.
      */
-    public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
+    private synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
         if (mLegacyVpnRunner == null) return null;
 
         final LegacyVpnInfo info = new LegacyVpnInfo();
@@ -2038,7 +2005,6 @@
 
         private void bringup() {
             // Catch all exceptions so we can clean up a few things.
-            boolean initFinished = false;
             try {
                 // Initialize the timer.
                 mBringupStartTime = SystemClock.elapsedRealtime();
@@ -2057,7 +2023,6 @@
                     throw new IllegalStateException("Cannot delete the state");
                 }
                 new File("/data/misc/vpn/abort").delete();
-                initFinished = true;
 
                 // Check if we need to restart any of the daemons.
                 boolean restart = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 88a1ef3..5b83f44 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2155,6 +2155,8 @@
 
             if (allNewUsers && !update) {
                 notifyPackageAdded(packageName, res.uid);
+            } else {
+                notifyPackageChanged(packageName, res.uid);
             }
 
             // Log current value of "unknown sources" setting
@@ -13762,6 +13764,22 @@
     }
 
     @Override
+    public void notifyPackageChanged(String packageName, int uid) {
+        final PackageListObserver[] observers;
+        synchronized (mPackages) {
+            if (mPackageListObservers.size() == 0) {
+                return;
+            }
+            final PackageListObserver[] observerArray =
+                    new PackageListObserver[mPackageListObservers.size()];
+            observers = mPackageListObservers.toArray(observerArray);
+        }
+        for (int i = observers.length - 1; i >= 0; --i) {
+            observers[i].onPackageChanged(packageName, uid);
+        }
+    }
+
+    @Override
     public void notifyPackageRemoved(String packageName, int uid) {
         final PackageListObserver[] observers;
         synchronized (mPackages) {
@@ -24987,5 +25005,6 @@
     void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
         boolean includeStopped, int appId, int[] userIds, int[] instantUserIds);
     void notifyPackageAdded(String packageName, int uid);
+    void notifyPackageChanged(String packageName, int uid);
     void notifyPackageRemoved(String packageName, int uid);
 }
diff --git a/services/net/Android.bp b/services/net/Android.bp
index fb548f9..8f8f9f9 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -67,7 +67,7 @@
     static_libs: [
         "dnsresolver_aidl_interface-V2-java",
         "ipmemorystore-client",
-        "netd_aidl_interface-V2-java",
+        "netd_aidl_interface-java",
         "networkstack-aidl-interfaces-V3-java",
     ],
 }
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
index 5a8e1e6..abb4666 100644
--- a/services/net/java/android/net/NetworkStackClient.java
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -191,7 +191,7 @@
         log("Network stack service start requested");
     }
 
-   /**
+    /**
      * Log a message in the local log.
      */
     private void log(@NonNull String message) {
@@ -302,5 +302,4 @@
         pw.println();
         pw.println("pendingNetStackRequests length: " + requestsQueueLength);
     }
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 68728af..6a03aed 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -60,6 +60,11 @@
             }
 
             @Override
+            public void notifyPackageChanged(String packageName, int uid) {
+
+            }
+
+            @Override
             public void notifyPackageRemoved(String packageName, int uid) {
             }
         }
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 76fc097..93b730c 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -4021,8 +4021,8 @@
         public static final String DEFAULT_SORT_ORDER = DELIVERY_TIME + " DESC";
 
         /**
-         * The Epoch Unix timestamp when the device received the message.
-         * <P>Type: INTEGER</P>
+         * The timestamp in millisecond of when the device received the message.
+         * <P>Type: BIGINT</P>
          */
         public static final String RECEIVED_TIME = "received_time";
 
@@ -4092,6 +4092,33 @@
                 CMAS_URGENCY,
                 CMAS_CERTAINTY
         };
+
+        /**
+         * Query columns for instantiating {@link android.telephony.SmsCbMessage} objects.
+         */
+        public static final String[] QUERY_COLUMNS_FWK = {
+                _ID,
+                GEOGRAPHICAL_SCOPE,
+                PLMN,
+                LAC,
+                CID,
+                SERIAL_NUMBER,
+                SERVICE_CATEGORY,
+                LANGUAGE_CODE,
+                MESSAGE_BODY,
+                MESSAGE_FORMAT,
+                MESSAGE_PRIORITY,
+                ETWS_WARNING_TYPE,
+                CMAS_MESSAGE_CLASS,
+                CMAS_CATEGORY,
+                CMAS_RESPONSE_TYPE,
+                CMAS_SEVERITY,
+                CMAS_URGENCY,
+                CMAS_CERTAINTY,
+                RECEIVED_TIME,
+                MESSAGE_BROADCASTED,
+                GEOMETRIES
+        };
     }
 
     /**
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index f03a9dc..af3ba5e 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -217,6 +217,9 @@
         }
 
         String scheme = uri.getScheme();
+        if (scheme == null) {
+            return null;
+        }
 
         if (scheme.equals("tel") || scheme.equals("sip")) {
             return uri.getSchemeSpecificPart();
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index d4ea29f..7aae5e4 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -48,7 +48,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
@@ -1555,15 +1554,13 @@
     public boolean
     deleteMessageFromIcc(int messageIndex) {
         boolean success = false;
-        byte[] pdu = new byte[SMS_RECORD_LENGTH-1];
-        Arrays.fill(pdu, (byte)0xff);
 
         try {
             ISms iSms = getISmsService();
             if (iSms != null) {
                 success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
                         ActivityThread.currentPackageName(),
-                        messageIndex, STATUS_ON_ICC_FREE, pdu);
+                        messageIndex, STATUS_ON_ICC_FREE, null /* pdu */);
             }
         } catch (RemoteException ex) {
             // ignore it
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d72ca6b..5bdfde5 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10926,24 +10926,28 @@
     }
 
     /**
-     * Get whether reboot is required or not after making changes to modem configurations.
+     * Get whether making changes to modem configurations by {@link #switchMultiSimConfig(int)} will
+     * trigger device reboot.
      * The modem configuration change refers to switching from single SIM configuration to DSDS
      * or the other way around.
-     * @Return {@code true} if reboot is required after making changes to modem configurations,
-     * otherwise return {@code false}.
      *
-     * @hide
+     *  <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return {@code true} if reboot will be triggered after making changes to modem
+     * configurations, otherwise return {@code false}.
      */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isRebootRequiredForModemConfigChange() {
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public boolean doesSwitchMultiSimConfigTriggerReboot() {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.isRebootRequiredForModemConfigChange();
+                return service.doesSwitchMultiSimConfigTriggerReboot(getSubId(),
+                        getOpPackageName());
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "isRebootRequiredForModemConfigChange RemoteException", e);
+            Log.e(TAG, "doesSwitchMultiSimConfigTriggerReboot RemoteException", e);
         }
         return false;
     }
diff --git a/telephony/java/com/android/internal/telephony/CbGeoUtils.java b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
index c973b67..73dd822 100644
--- a/telephony/java/com/android/internal/telephony/CbGeoUtils.java
+++ b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
@@ -53,6 +53,11 @@
 
     private static final String TAG = "CbGeoUtils";
 
+    /** The TLV tags of WAC, defined in ATIS-0700041 5.2.3 WAC tag coding. */
+    public static final int GEO_FENCING_MAXIMUM_WAIT_TIME = 0x01;
+    public static final int GEOMETRY_TYPE_POLYGON = 0x02;
+    public static final int GEOMETRY_TYPE_CIRCLE = 0x03;
+
     /** The identifier of geometry in the encoded string. */
     private static final String CIRCLE_SYMBOL = "circle";
     private static final String POLYGON_SYMBOL = "polygon";
@@ -92,6 +97,11 @@
                     + dlng * dlng * Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(p.lat));
             return 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x)) * EARTH_RADIUS_METER;
         }
+
+        @Override
+        public String toString() {
+            return "(" + lat + "," + lng + ")";
+        }
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index da80774..06f35a7 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1983,10 +1983,10 @@
     void switchMultiSimConfig(int numOfSims);
 
     /**
-     * Get if reboot is required upon altering modems configurations
+     * Get if altering modems configurations will trigger reboot.
      * @hide
      */
-    boolean isRebootRequiredForModemConfigChange();
+    boolean doesSwitchMultiSimConfigTriggerReboot(int subId, String callingPackage);
 
     /**
      * Get the mapping from logical slots to physical slots.
diff --git a/telephony/java/com/android/internal/telephony/SmsCbMessage.java b/telephony/java/com/android/internal/telephony/SmsCbMessage.java
index 046bf8c..b9edb9f 100644
--- a/telephony/java/com/android/internal/telephony/SmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/SmsCbMessage.java
@@ -16,8 +16,17 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
+import android.content.ContentValues;
+import android.database.Cursor;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.provider.Telephony.CellBroadcasts;
+
+import com.android.internal.telephony.CbGeoUtils;
+import com.android.internal.telephony.CbGeoUtils.Geometry;
+
+import java.util.List;
 
 /**
  * Parcelable object containing a received cell broadcast message. There are four different types
@@ -138,12 +147,31 @@
     /** CMAS warning notification information (CMAS warnings only). */
     private final SmsCbCmasInfo mCmasWarningInfo;
 
+    /** UNIX timestamp of when the message was received. */
+    private final long mReceivedTimeMillis;
+
+    /** CMAS warning area coordinates. */
+    private final List<Geometry> mGeometries;
+
     /**
      * Create a new SmsCbMessage with the specified data.
      */
     public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
             SmsCbLocation location, int serviceCategory, String language, String body,
             int priority, SmsCbEtwsInfo etwsWarningInfo, SmsCbCmasInfo cmasWarningInfo) {
+
+        this(messageFormat, geographicalScope, serialNumber, location, serviceCategory, language,
+                body, priority, etwsWarningInfo, cmasWarningInfo, null /* geometries */,
+                System.currentTimeMillis());
+    }
+
+    /**
+     * Create a new {@link SmsCbMessage} with the warning area coordinates information.
+     */
+    public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
+            SmsCbLocation location, int serviceCategory, String language, String body,
+            int priority, SmsCbEtwsInfo etwsWarningInfo, SmsCbCmasInfo cmasWarningInfo,
+            List<Geometry> geometries, long receivedTimeMillis) {
         mMessageFormat = messageFormat;
         mGeographicalScope = geographicalScope;
         mSerialNumber = serialNumber;
@@ -154,6 +182,8 @@
         mPriority = priority;
         mEtwsWarningInfo = etwsWarningInfo;
         mCmasWarningInfo = cmasWarningInfo;
+        mReceivedTimeMillis = receivedTimeMillis;
+        mGeometries = geometries;
     }
 
     /** Create a new SmsCbMessage object from a Parcel. */
@@ -184,6 +214,9 @@
                 mEtwsWarningInfo = null;
                 mCmasWarningInfo = null;
         }
+        mReceivedTimeMillis = in.readLong();
+        String geoStr = in.readString();
+        mGeometries = geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
     }
 
     /**
@@ -214,6 +247,9 @@
             // no ETWS or CMAS warning information
             dest.writeInt('0');
         }
+        dest.writeLong(mReceivedTimeMillis);
+        dest.writeString(
+                mGeometries != null ? CbGeoUtils.encodeGeometriesToString(mGeometries) : null);
     }
 
     public static final Parcelable.Creator<SmsCbMessage> CREATOR
@@ -293,6 +329,24 @@
     }
 
     /**
+     * Get the warning area coordinates information represent by polygons and circles.
+     * @return a list of geometries, {@link Nullable} means there is no coordinate information
+     * associated to this message.
+     */
+    @Nullable
+    public List<Geometry> getGeometries() {
+        return mGeometries;
+    }
+
+    /**
+     * Get the time when this message was received.
+     * @return the time in millisecond
+     */
+    public long getReceivedTime() {
+        return mReceivedTimeMillis;
+    }
+
+    /**
      * Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}).
      * @return an integer representing 3GPP or 3GPP2 message format
      */
@@ -368,7 +422,10 @@
                 + mServiceCategory + ", language=" + mLanguage + ", body=" + mBody
                 + ", priority=" + mPriority
                 + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
-                + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "") + '}';
+                + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "")
+                + ", geo=" + (mGeometries != null
+                ? CbGeoUtils.encodeGeometriesToString(mGeometries) : "null")
+                + '}';
     }
 
     /**
@@ -379,4 +436,171 @@
     public int describeContents() {
         return 0;
     }
+
+    /**
+     * @return the {@link ContentValues} instance that includes the cell broadcast data.
+     */
+    public ContentValues getContentValues() {
+        ContentValues cv = new ContentValues(16);
+        cv.put(CellBroadcasts.GEOGRAPHICAL_SCOPE, mGeographicalScope);
+        if (mLocation.getPlmn() != null) {
+            cv.put(CellBroadcasts.PLMN, mLocation.getPlmn());
+        }
+        if (mLocation.getLac() != -1) {
+            cv.put(CellBroadcasts.LAC, mLocation.getLac());
+        }
+        if (mLocation.getCid() != -1) {
+            cv.put(CellBroadcasts.CID, mLocation.getCid());
+        }
+        cv.put(CellBroadcasts.SERIAL_NUMBER, getSerialNumber());
+        cv.put(CellBroadcasts.SERVICE_CATEGORY, getServiceCategory());
+        cv.put(CellBroadcasts.LANGUAGE_CODE, getLanguageCode());
+        cv.put(CellBroadcasts.MESSAGE_BODY, getMessageBody());
+        cv.put(CellBroadcasts.MESSAGE_FORMAT, getMessageFormat());
+        cv.put(CellBroadcasts.MESSAGE_PRIORITY, getMessagePriority());
+
+        SmsCbEtwsInfo etwsInfo = getEtwsWarningInfo();
+        if (etwsInfo != null) {
+            cv.put(CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType());
+        }
+
+        SmsCbCmasInfo cmasInfo = getCmasWarningInfo();
+        if (cmasInfo != null) {
+            cv.put(CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass());
+            cv.put(CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory());
+            cv.put(CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType());
+            cv.put(CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity());
+            cv.put(CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency());
+            cv.put(CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty());
+        }
+
+        cv.put(CellBroadcasts.RECEIVED_TIME, mReceivedTimeMillis);
+
+        if (mGeometries != null) {
+            cv.put(CellBroadcasts.GEOMETRIES, CbGeoUtils.encodeGeometriesToString(mGeometries));
+        } else {
+            cv.put(CellBroadcasts.GEOMETRIES, (String) null);
+        }
+
+        return cv;
+    }
+
+    /**
+     * Create a {@link SmsCbMessage} instance from a row in the cell broadcast database.
+     * @param cursor an open SQLite cursor pointing to the row to read
+     * @return a {@link SmsCbMessage} instance.
+     * @throws IllegalArgumentException if one of the required columns is missing
+     */
+    public static SmsCbMessage createFromCursor(Cursor cursor) {
+        int geoScope = cursor.getInt(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.GEOGRAPHICAL_SCOPE));
+        int serialNum = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERIAL_NUMBER));
+        int category = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERVICE_CATEGORY));
+        String language = cursor.getString(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.LANGUAGE_CODE));
+        String body = cursor.getString(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_BODY));
+        int format = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT));
+        int priority = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY));
+
+        String plmn;
+        int plmnColumn = cursor.getColumnIndex(CellBroadcasts.PLMN);
+        if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) {
+            plmn = cursor.getString(plmnColumn);
+        } else {
+            plmn = null;
+        }
+
+        int lac;
+        int lacColumn = cursor.getColumnIndex(CellBroadcasts.LAC);
+        if (lacColumn != -1 && !cursor.isNull(lacColumn)) {
+            lac = cursor.getInt(lacColumn);
+        } else {
+            lac = -1;
+        }
+
+        int cid;
+        int cidColumn = cursor.getColumnIndex(CellBroadcasts.CID);
+        if (cidColumn != -1 && !cursor.isNull(cidColumn)) {
+            cid = cursor.getInt(cidColumn);
+        } else {
+            cid = -1;
+        }
+
+        SmsCbLocation location = new SmsCbLocation(plmn, lac, cid);
+
+        SmsCbEtwsInfo etwsInfo;
+        int etwsWarningTypeColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_WARNING_TYPE);
+        if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) {
+            int warningType = cursor.getInt(etwsWarningTypeColumn);
+            etwsInfo = new SmsCbEtwsInfo(warningType, false, false, false, null);
+        } else {
+            etwsInfo = null;
+        }
+
+        SmsCbCmasInfo cmasInfo = null;
+        int cmasMessageClassColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_MESSAGE_CLASS);
+        if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) {
+            int messageClass = cursor.getInt(cmasMessageClassColumn);
+
+            int cmasCategory;
+            int cmasCategoryColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CATEGORY);
+            if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) {
+                cmasCategory = cursor.getInt(cmasCategoryColumn);
+            } else {
+                cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
+            }
+
+            int responseType;
+            int cmasResponseTypeColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_RESPONSE_TYPE);
+            if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) {
+                responseType = cursor.getInt(cmasResponseTypeColumn);
+            } else {
+                responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
+            }
+
+            int severity;
+            int cmasSeverityColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_SEVERITY);
+            if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) {
+                severity = cursor.getInt(cmasSeverityColumn);
+            } else {
+                severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
+            }
+
+            int urgency;
+            int cmasUrgencyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_URGENCY);
+            if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) {
+                urgency = cursor.getInt(cmasUrgencyColumn);
+            } else {
+                urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
+            }
+
+            int certainty;
+            int cmasCertaintyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CERTAINTY);
+            if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) {
+                certainty = cursor.getInt(cmasCertaintyColumn);
+            } else {
+                certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
+            }
+
+            cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity,
+                    urgency, certainty);
+        }
+
+        String geoStr = cursor.getString(cursor.getColumnIndex(CellBroadcasts.GEOMETRIES));
+        List<Geometry> geometries =
+                geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
+
+        long receivedTimeSec = cursor.getLong(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.RECEIVED_TIME));
+
+        return new SmsCbMessage(format, geoScope, serialNum, location, category,
+                language, body, priority, etwsInfo, cmasInfo, geometries, receivedTimeSec);
+    }
+
+    /**
+     * @return {@code True} if this message needs geo-fencing check.
+     */
+    public boolean needGeoFencingCheck() {
+        return mGeometries != null;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index 8015b07..dca4e6b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -22,58 +22,36 @@
 import static android.telephony.SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE;
 import static android.telephony.SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.telephony.SmsCbLocation;
 import android.telephony.SmsCbMessage;
 import android.util.Pair;
+import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.internal.telephony.CbGeoUtils;
+import com.android.internal.telephony.CbGeoUtils.Circle;
+import com.android.internal.telephony.CbGeoUtils.Geometry;
+import com.android.internal.telephony.CbGeoUtils.LatLng;
+import com.android.internal.telephony.CbGeoUtils.Polygon;
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.gsm.GsmSmsCbMessage.GeoFencingTriggerMessage.CellBroadcastIdentity;
+import com.android.internal.telephony.gsm.SmsCbHeader.DataCodingScheme;
 
 import java.io.UnsupportedEncodingException;
-import java.util.Locale;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is
  * public because {@link #createSmsCbMessage(SmsCbLocation, byte[][])} is used by some test cases.
  */
 public class GsmSmsCbMessage {
-
-    /**
-     * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
-     */
-    private static final String[] LANGUAGE_CODES_GROUP_0 = {
-            Locale.GERMAN.getLanguage(),        // German
-            Locale.ENGLISH.getLanguage(),       // English
-            Locale.ITALIAN.getLanguage(),       // Italian
-            Locale.FRENCH.getLanguage(),        // French
-            new Locale("es").getLanguage(),     // Spanish
-            new Locale("nl").getLanguage(),     // Dutch
-            new Locale("sv").getLanguage(),     // Swedish
-            new Locale("da").getLanguage(),     // Danish
-            new Locale("pt").getLanguage(),     // Portuguese
-            new Locale("fi").getLanguage(),     // Finnish
-            new Locale("nb").getLanguage(),     // Norwegian
-            new Locale("el").getLanguage(),     // Greek
-            new Locale("tr").getLanguage(),     // Turkish
-            new Locale("hu").getLanguage(),     // Hungarian
-            new Locale("pl").getLanguage(),     // Polish
-            null
-    };
-
-    /**
-     * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
-     */
-    private static final String[] LANGUAGE_CODES_GROUP_2 = {
-            new Locale("cs").getLanguage(),     // Czech
-            new Locale("he").getLanguage(),     // Hebrew
-            new Locale("ar").getLanguage(),     // Arabic
-            new Locale("ru").getLanguage(),     // Russian
-            new Locale("is").getLanguage(),     // Icelandic
-            null, null, null, null, null, null, null, null, null, null, null
-    };
+    private static final String TAG = GsmSmsCbMessage.class.getSimpleName();
 
     private static final char CARRIAGE_RETURN = 0x0d;
 
@@ -114,8 +92,9 @@
      * @param pdus PDU bytes
      */
     public static SmsCbMessage createSmsCbMessage(Context context, SmsCbHeader header,
-                                                  SmsCbLocation location, byte[][] pdus)
+            SmsCbLocation location, byte[][] pdus)
             throws IllegalArgumentException {
+        long receivedTimeMillis = System.currentTimeMillis();
         if (header.isEtwsPrimaryNotification()) {
             // ETSI TS 23.041 ETWS Primary Notification message
             // ETWS primary message only contains 4 fields including serial number,
@@ -125,12 +104,41 @@
                     header.getSerialNumber(), location, header.getServiceCategory(), null,
                     getEtwsPrimaryMessage(context, header.getEtwsInfo().getWarningType()),
                     SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, header.getEtwsInfo(),
-                    header.getCmasInfo());
+                    header.getCmasInfo(), null /* geometries */, receivedTimeMillis);
+        } else if (header.isUmtsFormat()) {
+            // UMTS format has only 1 PDU
+            byte[] pdu = pdus[0];
+            Pair<String, String> cbData = parseUmtsBody(header, pdu);
+            String language = cbData.first;
+            String body = cbData.second;
+            int priority = header.isEmergencyMessage() ? SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY
+                    : SmsCbMessage.MESSAGE_PRIORITY_NORMAL;
+            int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
+            int wacDataOffset = SmsCbHeader.PDU_HEADER_LENGTH
+                    + 1 // number of pages
+                    + (PDU_BODY_PAGE_LENGTH + 1) * nrPages; // cb data
+
+            // Has Warning Area Coordinates information
+            List<Geometry> geometries = null;
+            if (pdu.length > wacDataOffset) {
+                try {
+                    geometries = parseWarningAreaCoordinates(pdu, wacDataOffset);
+                } catch (Exception ex) {
+                    // Catch the exception here, the message will be considered as having no WAC
+                    // information which means the message will be broadcasted directly.
+                    Slog.e(TAG, "Can't parse warning area coordinates, ex = " + ex.toString());
+                }
+            }
+
+            return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
+                    header.getGeographicalScope(), header.getSerialNumber(), location,
+                    header.getServiceCategory(), language, body, priority,
+                    header.getEtwsInfo(), header.getCmasInfo(), geometries, receivedTimeMillis);
         } else {
             String language = null;
             StringBuilder sb = new StringBuilder();
             for (byte[] pdu : pdus) {
-                Pair<String, String> p = parseBody(header, pdu);
+                Pair<String, String> p = parseGsmBody(header, pdu);
                 language = p.first;
                 sb.append(p.second);
             }
@@ -140,154 +148,197 @@
             return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
                     header.getGeographicalScope(), header.getSerialNumber(), location,
                     header.getServiceCategory(), language, sb.toString(), priority,
-                    header.getEtwsInfo(), header.getCmasInfo());
+                    header.getEtwsInfo(), header.getCmasInfo(), null /* geometries */,
+                    receivedTimeMillis);
         }
     }
 
     /**
-     * Parse and unpack the body text according to the encoding in the DCS.
-     * After completing successfully this method will have assigned the body
-     * text into mBody, and optionally the language code into mLanguage
+     * Parse WEA Handset Action Message(WHAM) a.k.a geo-fencing trigger message.
+     *
+     * WEA Handset Action Message(WHAM) is a cell broadcast service message broadcast by the network
+     * to direct devices to perform a geo-fencing check on selected alerts.
+     *
+     * WEA Handset Action Message(WHAM) requirements from ATIS-0700041 section 4
+     * 1. The Warning Message contents of a WHAM shall be in Cell Broadcast(CB) data format as
+     * defined in TS 23.041.
+     * 2. The Warning Message Contents of WHAM shall be limited to one CB page(max 20 referenced
+     * WEA messages).
+     * 3. The broadcast area for a WHAM shall be the union of the broadcast areas of the referenced
+     * WEA message.
+     * @param pdu cell broadcast pdu, including the header
+     * @return {@link GeoFencingTriggerMessage} instance
+     */
+    public static GeoFencingTriggerMessage createGeoFencingTriggerMessage(byte[] pdu) {
+        try {
+            // Header length + 1(number of page). ATIS-0700041 define the number of page of
+            // geo-fencing trigger message is 1.
+            int whamOffset = SmsCbHeader.PDU_HEADER_LENGTH + 1;
+
+            BitStreamReader bitReader = new BitStreamReader(pdu, whamOffset);
+            int type = bitReader.read(4);
+            int length = bitReader.read(7);
+            // Skip the remained 5 bits
+            bitReader.skip();
+
+            int messageIdentifierCount = (length - 2) * 8 / 32;
+            List<CellBroadcastIdentity> cbIdentifiers = new ArrayList<>();
+            for (int i = 0; i < messageIdentifierCount; i++) {
+                // Both messageIdentifier and serialNumber are 16 bits integers.
+                // ATIS-0700041 Section 5.1.6
+                int messageIdentifier = bitReader.read(16);
+                int serialNumber = bitReader.read(16);
+                cbIdentifiers.add(new CellBroadcastIdentity(messageIdentifier, serialNumber));
+            }
+            return new GeoFencingTriggerMessage(type, cbIdentifiers);
+        } catch (Exception ex) {
+            Slog.e(TAG, "create geo-fencing trigger failed, ex = " + ex.toString());
+            return null;
+        }
+    }
+
+    private static List<Geometry> parseWarningAreaCoordinates(byte[] pdu, int wacOffset) {
+        // little-endian
+        int wacDataLength = (pdu[wacOffset + 1] << 8) | pdu[wacOffset];
+        int offset = wacOffset + 2;
+
+        if (offset + wacDataLength > pdu.length) {
+            throw new IllegalArgumentException("Invalid wac data, expected the length of pdu at"
+                    + "least " + offset + wacDataLength + ", actual is " + pdu.length);
+        }
+
+        BitStreamReader bitReader = new BitStreamReader(pdu, offset);
+
+        List<Geometry> geo = new ArrayList<>();
+        int remainedBytes = wacDataLength;
+        while (remainedBytes > 0) {
+            int type = bitReader.read(4);
+            int length = bitReader.read(10);
+            remainedBytes -= length;
+            // Skip the 2 remained bits
+            bitReader.skip();
+
+            switch (type) {
+                case CbGeoUtils.GEO_FENCING_MAXIMUM_WAIT_TIME:
+                    // TODO: handle the maximum wait time in cell broadcast provider.
+                    int maximumWaitTimeSec = bitReader.read(8);
+                    break;
+                case CbGeoUtils.GEOMETRY_TYPE_POLYGON:
+                    List<LatLng> latLngs = new ArrayList<>();
+                    // Each coordinate is represented by 44 bits integer.
+                    // ATIS-0700041 5.2.4 Coordinate coding
+                    int n = (length - 2) * 8 / 44;
+                    for (int i = 0; i < n; i++) {
+                        latLngs.add(getLatLng(bitReader));
+                    }
+                    // Skip the padding bits
+                    bitReader.skip();
+                    geo.add(new Polygon(latLngs));
+                    break;
+                case CbGeoUtils.GEOMETRY_TYPE_CIRCLE:
+                    LatLng center = getLatLng(bitReader);
+                    // radius = (wacRadius / 2^6). The unit of wacRadius is km, we use meter as the
+                    // distance unit during geo-fencing.
+                    // ATIS-0700041 5.2.5 radius coding
+                    double radius = (bitReader.read(20) * 1.0 / (1 << 6)) * 1000.0;
+                    geo.add(new Circle(center, radius));
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported geoType = " + type);
+            }
+        }
+        return geo;
+    }
+
+    /**
+     * The coordinate is (latitude, longitude), represented by a 44 bits integer.
+     * The coding is defined in ATIS-0700041 5.2.4
+     * @param bitReader
+     * @return coordinate (latitude, longitude)
+     */
+    private static LatLng getLatLng(BitStreamReader bitReader) {
+        // wacLatitude = floor(((latitude + 90) / 180) * 2^22)
+        // wacLongitude = floor(((longitude + 180) / 360) * 2^22)
+        int wacLat = bitReader.read(22);
+        int wacLng = bitReader.read(22);
+
+        // latitude = wacLatitude * 180 / 2^22 - 90
+        // longitude = wacLongitude * 360 / 2^22 -180
+        return new LatLng((wacLat * 180.0 / (1 << 22)) - 90, (wacLng * 360.0 / (1 << 22) - 180));
+    }
+
+    /**
+     * Parse and unpack the UMTS body text according to the encoding in the data coding scheme.
      *
      * @param header the message header to use
      * @param pdu the PDU to decode
-     * @return a Pair of Strings containing the language and body of the message
+     * @return a pair of string containing the language and body of the message in order
      */
-    private static Pair<String, String> parseBody(SmsCbHeader header, byte[] pdu) {
-        int encoding;
-        String language = null;
-        boolean hasLanguageIndicator = false;
-        int dataCodingScheme = header.getDataCodingScheme();
+    private static Pair<String, String> parseUmtsBody(SmsCbHeader header, byte[] pdu) {
+        // Payload may contain multiple pages
+        int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
+        String language = header.getDataCodingSchemeStructedData().language;
 
-        // Extract encoding and language from DCS, as defined in 3gpp TS 23.038,
-        // section 5.
-        switch ((dataCodingScheme & 0xf0) >> 4) {
-            case 0x00:
-                encoding = SmsConstants.ENCODING_7BIT;
-                language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f];
-                break;
-
-            case 0x01:
-                hasLanguageIndicator = true;
-                if ((dataCodingScheme & 0x0f) == 0x01) {
-                    encoding = SmsConstants.ENCODING_16BIT;
-                } else {
-                    encoding = SmsConstants.ENCODING_7BIT;
-                }
-                break;
-
-            case 0x02:
-                encoding = SmsConstants.ENCODING_7BIT;
-                language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f];
-                break;
-
-            case 0x03:
-                encoding = SmsConstants.ENCODING_7BIT;
-                break;
-
-            case 0x04:
-            case 0x05:
-                switch ((dataCodingScheme & 0x0c) >> 2) {
-                    case 0x01:
-                        encoding = SmsConstants.ENCODING_8BIT;
-                        break;
-
-                    case 0x02:
-                        encoding = SmsConstants.ENCODING_16BIT;
-                        break;
-
-                    case 0x00:
-                    default:
-                        encoding = SmsConstants.ENCODING_7BIT;
-                        break;
-                }
-                break;
-
-            case 0x06:
-            case 0x07:
-                // Compression not supported
-            case 0x09:
-                // UDH structure not supported
-            case 0x0e:
-                // Defined by the WAP forum not supported
-                throw new IllegalArgumentException("Unsupported GSM dataCodingScheme "
-                        + dataCodingScheme);
-
-            case 0x0f:
-                if (((dataCodingScheme & 0x04) >> 2) == 0x01) {
-                    encoding = SmsConstants.ENCODING_8BIT;
-                } else {
-                    encoding = SmsConstants.ENCODING_7BIT;
-                }
-                break;
-
-            default:
-                // Reserved values are to be treated as 7-bit
-                encoding = SmsConstants.ENCODING_7BIT;
-                break;
+        if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1)
+                * nrPages) {
+            throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match "
+                    + nrPages + " pages");
         }
 
-        if (header.isUmtsFormat()) {
-            // Payload may contain multiple pages
-            int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
+        StringBuilder sb = new StringBuilder();
 
-            if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1)
-                    * nrPages) {
-                throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match "
-                        + nrPages + " pages");
+        for (int i = 0; i < nrPages; i++) {
+            // Each page is 82 bytes followed by a length octet indicating
+            // the number of useful octets within those 82
+            int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i;
+            int length = pdu[offset + PDU_BODY_PAGE_LENGTH];
+
+            if (length > PDU_BODY_PAGE_LENGTH) {
+                throw new IllegalArgumentException("Page length " + length
+                        + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH);
             }
 
-            StringBuilder sb = new StringBuilder();
-
-            for (int i = 0; i < nrPages; i++) {
-                // Each page is 82 bytes followed by a length octet indicating
-                // the number of useful octets within those 82
-                int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i;
-                int length = pdu[offset + PDU_BODY_PAGE_LENGTH];
-
-                if (length > PDU_BODY_PAGE_LENGTH) {
-                    throw new IllegalArgumentException("Page length " + length
-                            + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH);
-                }
-
-                Pair<String, String> p = unpackBody(pdu, encoding, offset, length,
-                        hasLanguageIndicator, language);
-                language = p.first;
-                sb.append(p.second);
-            }
-            return new Pair<String, String>(language, sb.toString());
-        } else {
-            // Payload is one single page
-            int offset = SmsCbHeader.PDU_HEADER_LENGTH;
-            int length = pdu.length - offset;
-
-            return unpackBody(pdu, encoding, offset, length, hasLanguageIndicator, language);
+            Pair<String, String> p = unpackBody(pdu, offset, length,
+                    header.getDataCodingSchemeStructedData());
+            language = p.first;
+            sb.append(p.second);
         }
+        return new Pair(language, sb.toString());
+
     }
 
     /**
-     * Unpack body text from the pdu using the given encoding, position and
-     * length within the pdu
+     * Parse and unpack the GSM body text according to the encoding in the data coding scheme.
+     * @param header the message header to use
+     * @param pdu the PDU to decode
+     * @return a pair of string containing the language and body of the message in order
+     */
+    private static Pair<String, String> parseGsmBody(SmsCbHeader header, byte[] pdu) {
+        // Payload is one single page
+        int offset = SmsCbHeader.PDU_HEADER_LENGTH;
+        int length = pdu.length - offset;
+        return unpackBody(pdu, offset, length, header.getDataCodingSchemeStructedData());
+    }
+
+    /**
+     * Unpack body text from the pdu using the given encoding, position and length within the pdu.
      *
      * @param pdu The pdu
-     * @param encoding The encoding, as derived from the DCS
      * @param offset Position of the first byte to unpack
      * @param length Number of bytes to unpack
-     * @param hasLanguageIndicator true if the body text is preceded by a
-     *            language indicator. If so, this method will as a side-effect
-     *            assign the extracted language code into mLanguage
-     * @param language the language to return if hasLanguageIndicator is false
+     * @param dcs data coding scheme
      * @return a Pair of Strings containing the language and body of the message
      */
-    private static Pair<String, String> unpackBody(byte[] pdu, int encoding, int offset, int length,
-            boolean hasLanguageIndicator, String language) {
+    private static Pair<String, String> unpackBody(byte[] pdu, int offset, int length,
+            DataCodingScheme dcs) {
         String body = null;
 
-        switch (encoding) {
+        String language = dcs.language;
+        switch (dcs.encoding) {
             case SmsConstants.ENCODING_7BIT:
                 body = GsmAlphabet.gsm7BitPackedToString(pdu, offset, length * 8 / 7);
 
-                if (hasLanguageIndicator && body != null && body.length() > 2) {
+                if (dcs.hasLanguageIndicator && body != null && body.length() > 2) {
                     // Language is two GSM characters followed by a CR.
                     // The actual body text is offset by 3 characters.
                     language = body.substring(0, 2);
@@ -296,7 +347,7 @@
                 break;
 
             case SmsConstants.ENCODING_16BIT:
-                if (hasLanguageIndicator && pdu.length >= offset + 2) {
+                if (dcs.hasLanguageIndicator && pdu.length >= offset + 2) {
                     // Language is two GSM characters.
                     // The actual body text is offset by 2 bytes.
                     language = GsmAlphabet.gsm7BitPackedToString(pdu, offset, 2);
@@ -330,4 +381,105 @@
 
         return new Pair<String, String>(language, body);
     }
+
+    /** A class use to facilitate the processing of bits stream data. */
+    private static final class BitStreamReader {
+        /** The bits stream represent by a bytes array. */
+        private final byte[] mData;
+
+        /** The offset of the current byte. */
+        private int mCurrentOffset;
+
+        /**
+         * The remained bits of the current byte which have not been read. The most significant
+         * will be read first, so the remained bits are always the least significant bits.
+         */
+        private int mRemainedBit;
+
+        /**
+         * Constructor
+         * @param data bit stream data represent by byte array.
+         * @param offset the offset of the first byte.
+         */
+        BitStreamReader(byte[] data, int offset) {
+            mData = data;
+            mCurrentOffset = offset;
+            mRemainedBit = 8;
+        }
+
+        /**
+         * Read the first {@code count} bits.
+         * @param count the number of bits need to read
+         * @return {@code bits} represent by an 32-bits integer, therefore {@code count} must be no
+         * greater than 32.
+         */
+        public int read(int count) throws IndexOutOfBoundsException {
+            int val = 0;
+            while (count > 0) {
+                if (count >= mRemainedBit) {
+                    val <<= mRemainedBit;
+                    val |= mData[mCurrentOffset] & ((1 << mRemainedBit) - 1);
+                    count -= mRemainedBit;
+                    mRemainedBit = 8;
+                    ++mCurrentOffset;
+                } else {
+                    val <<= count;
+                    val |= (mData[mCurrentOffset] & ((1 << mRemainedBit) - 1))
+                            >> (mRemainedBit - count);
+                    mRemainedBit -= count;
+                    count = 0;
+                }
+            }
+            return val;
+        }
+
+        /**
+         * Skip the current bytes if the remained bits is less than 8. This is useful when
+         * processing the padding or reserved bits.
+         */
+        public void skip() {
+            if (mRemainedBit < 8) {
+                mRemainedBit = 8;
+                ++mCurrentOffset;
+            }
+        }
+    }
+
+    static final class GeoFencingTriggerMessage {
+        /**
+         * Indicate the list of active alerts share their warning area coordinates which means the
+         * broadcast area is the union of the broadcast areas of the active alerts in this list.
+         */
+        public static final int TYPE_ACTIVE_ALERT_SHARE_WAC = 2;
+
+        public final int type;
+        public final List<CellBroadcastIdentity> cbIdentifiers;
+
+        GeoFencingTriggerMessage(int type, @NonNull List<CellBroadcastIdentity> cbIdentifiers) {
+            this.type = type;
+            this.cbIdentifiers = cbIdentifiers;
+        }
+
+        boolean shouldShareBroadcastArea() {
+            return type == TYPE_ACTIVE_ALERT_SHARE_WAC;
+        }
+
+        static final class CellBroadcastIdentity {
+            public final int messageIdentifier;
+            public final int serialNumber;
+            CellBroadcastIdentity(int messageIdentifier, int serialNumber) {
+                this.messageIdentifier = messageIdentifier;
+                this.serialNumber = serialNumber;
+            }
+        }
+
+        @Override
+        public String toString() {
+            String identifiers = cbIdentifiers.stream()
+                    .map(cbIdentifier ->String.format("(msgId = %d, serial = %d)",
+                            cbIdentifier.messageIdentifier, cbIdentifier.serialNumber))
+                    .collect(Collectors.joining(","));
+            return "triggerType=" + type + " identifiers=" + identifiers;
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
index 541ca8d..5ad2b9d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
@@ -215,9 +215,11 @@
     public static final int MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST_LANGUAGE
             = 0x112F; // 4399
 
-    /** End of CMAS Message Identifier range (including future extensions). */
-    public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER
-            = 0x112F; // 4399
+    /** CMAS Message Identifier for CMAS geo fencing trigger message. */
+    public static final int MESSAGE_ID_CMAS_GEO_FENCING_TRIGGER = 0x1130; // 4440
+
+    /** End of CMAS Message Identifier range. */
+    public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER = MESSAGE_ID_CMAS_GEO_FENCING_TRIGGER;
 
     /** End of PWS Message Identifier range (includes ETWS, CMAS, and future extensions). */
     public static final int MESSAGE_ID_PWS_LAST_IDENTIFIER
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 0dbc186..acdc838 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -19,7 +19,10 @@
 import android.telephony.SmsCbCmasInfo;
 import android.telephony.SmsCbEtwsInfo;
 
+import com.android.internal.telephony.SmsConstants;
+
 import java.util.Arrays;
+import java.util.Locale;
 
 /**
  * Parses a 3GPP TS 23.041 cell broadcast message header. This class is public for use by
@@ -32,6 +35,39 @@
  * The raw PDU is no longer sent to SMS CB applications.
  */
 public class SmsCbHeader {
+    /**
+     * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
+     */
+    private static final String[] LANGUAGE_CODES_GROUP_0 = {
+            Locale.GERMAN.getLanguage(),        // German
+            Locale.ENGLISH.getLanguage(),       // English
+            Locale.ITALIAN.getLanguage(),       // Italian
+            Locale.FRENCH.getLanguage(),        // French
+            new Locale("es").getLanguage(),     // Spanish
+            new Locale("nl").getLanguage(),     // Dutch
+            new Locale("sv").getLanguage(),     // Swedish
+            new Locale("da").getLanguage(),     // Danish
+            new Locale("pt").getLanguage(),     // Portuguese
+            new Locale("fi").getLanguage(),     // Finnish
+            new Locale("nb").getLanguage(),     // Norwegian
+            new Locale("el").getLanguage(),     // Greek
+            new Locale("tr").getLanguage(),     // Turkish
+            new Locale("hu").getLanguage(),     // Hungarian
+            new Locale("pl").getLanguage(),     // Polish
+            null
+    };
+
+    /**
+     * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
+     */
+    private static final String[] LANGUAGE_CODES_GROUP_2 = {
+            new Locale("cs").getLanguage(),     // Czech
+            new Locale("he").getLanguage(),     // Hebrew
+            new Locale("ar").getLanguage(),     // Arabic
+            new Locale("ru").getLanguage(),     // Russian
+            new Locale("is").getLanguage(),     // Icelandic
+            null, null, null, null, null, null, null, null, null, null, null
+    };
 
     /**
      * Length of SMS-CB header
@@ -84,6 +120,8 @@
 
     private final int mFormat;
 
+    private DataCodingScheme mDataCodingSchemeStructedData;
+
     /** ETWS warning notification info. */
     private final SmsCbEtwsInfo mEtwsInfo;
 
@@ -162,6 +200,10 @@
             mNrOfPages = 1;
         }
 
+        if (mDataCodingScheme != -1) {
+            mDataCodingSchemeStructedData = new DataCodingScheme(mDataCodingScheme);
+        }
+
         if (isEtwsMessage()) {
             boolean emergencyUserAlert = isEtwsEmergencyUserAlert();
             boolean activatePopup = isEtwsPopupAlert();
@@ -199,6 +241,10 @@
         return mDataCodingScheme;
     }
 
+    DataCodingScheme getDataCodingSchemeStructedData() {
+        return mDataCodingSchemeStructedData;
+    }
+
     int getPageIndex() {
         return mPageIndex;
     }
@@ -448,4 +494,93 @@
                 + ", DCS=0x" + Integer.toHexString(mDataCodingScheme)
                 + ", page " + mPageIndex + " of " + mNrOfPages + '}';
     }
+
+    /**
+     * CBS Data Coding Scheme.
+     * Reference: 3GPP TS 23.038 version 15.0.0 section #5, CBS Data Coding Scheme
+     */
+    public static final class DataCodingScheme {
+        public final int encoding;
+        public final String language;
+        public final boolean hasLanguageIndicator;
+
+        public DataCodingScheme(int dataCodingScheme) {
+            int encoding = 0;
+            String language = null;
+            boolean hasLanguageIndicator = false;
+
+            // Extract encoding and language from DCS, as defined in 3gpp TS 23.038,
+            // section 5.
+            switch ((dataCodingScheme & 0xf0) >> 4) {
+                case 0x00:
+                    encoding = SmsConstants.ENCODING_7BIT;
+                    language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f];
+                    break;
+
+                case 0x01:
+                    hasLanguageIndicator = true;
+                    if ((dataCodingScheme & 0x0f) == 0x01) {
+                        encoding = SmsConstants.ENCODING_16BIT;
+                    } else {
+                        encoding = SmsConstants.ENCODING_7BIT;
+                    }
+                    break;
+
+                case 0x02:
+                    encoding = SmsConstants.ENCODING_7BIT;
+                    language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f];
+                    break;
+
+                case 0x03:
+                    encoding = SmsConstants.ENCODING_7BIT;
+                    break;
+
+                case 0x04:
+                case 0x05:
+                    switch ((dataCodingScheme & 0x0c) >> 2) {
+                        case 0x01:
+                            encoding = SmsConstants.ENCODING_8BIT;
+                            break;
+
+                        case 0x02:
+                            encoding = SmsConstants.ENCODING_16BIT;
+                            break;
+
+                        case 0x00:
+                        default:
+                            encoding = SmsConstants.ENCODING_7BIT;
+                            break;
+                    }
+                    break;
+
+                case 0x06:
+                case 0x07:
+                    // Compression not supported
+                case 0x09:
+                    // UDH structure not supported
+                case 0x0e:
+                    // Defined by the WAP forum not supported
+                    throw new IllegalArgumentException("Unsupported GSM dataCodingScheme "
+                            + dataCodingScheme);
+
+                case 0x0f:
+                    if (((dataCodingScheme & 0x04) >> 2) == 0x01) {
+                        encoding = SmsConstants.ENCODING_8BIT;
+                    } else {
+                        encoding = SmsConstants.ENCODING_7BIT;
+                    }
+                    break;
+
+                default:
+                    // Reserved values are to be treated as 7-bit
+                    encoding = SmsConstants.ENCODING_7BIT;
+                    break;
+            }
+
+
+            this.encoding = encoding;
+            this.language = language;
+            this.hasLanguageIndicator = hasLanguageIndicator;
+        }
+    }
 }
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 5667387..17e3bac 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -1218,6 +1218,7 @@
 
         int encodingType = ENCODING_UNKNOWN;
 
+        Resources r = Resources.getSystem();
         // Look up the data encoding scheme
         if ((mDataCodingScheme & 0x80) == 0) {
             userDataCompressed = (0 != (mDataCodingScheme & 0x20));
@@ -1239,7 +1240,6 @@
                 case 1: // 8 bit data
                     //Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet string
                     //that's stored in 8-bit unpacked format) characters.
-                    Resources r = Resources.getSystem();
                     if (r.getBoolean(com.android.internal.
                             R.bool.config_sms_decode_gsm_8bit_data)) {
                         encodingType = ENCODING_8BIT;
@@ -1249,7 +1249,8 @@
                 case 3: // reserved
                     Rlog.w(LOG_TAG, "1 - Unsupported SMS data coding scheme "
                             + (mDataCodingScheme & 0xff));
-                    encodingType = ENCODING_8BIT;
+                    encodingType = r.getInteger(
+                            com.android.internal.R.integer.default_reserved_data_coding_scheme);
                     break;
                 }
             }
@@ -1403,7 +1404,6 @@
         case ENCODING_8BIT:
             //Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet string
             //that's stored in 8-bit unpacked format) characters.
-            Resources r = Resources.getSystem();
             if (r.getBoolean(com.android.internal.
                     R.bool.config_sms_decode_gsm_8bit_data)) {
                 mMessageBody = p.getUserDataGSM8bit(count);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 2aed2de0..70e5160 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2563,6 +2563,7 @@
 
         private final CloseGuard mCloseGuard = CloseGuard.get();
         private final WifiConfiguration mConfig;
+        private boolean mClosed = false;
 
         /** @hide */
         @VisibleForTesting
@@ -2578,8 +2579,13 @@
         @Override
         public void close() {
             try {
-                stopLocalOnlyHotspot();
-                mCloseGuard.close();
+                synchronized (mLock) {
+                    if (!mClosed) {
+                        mClosed = true;
+                        stopLocalOnlyHotspot();
+                        mCloseGuard.close();
+                    }
+                }
             } catch (Exception e) {
                 Log.e(TAG, "Failed to stop Local Only Hotspot.");
             }