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.");
}