TelephonyManager Carrier Network Change Notification
Adds a way for a carrier app to notify the system that an intended network
change is starting or ending. This can be used by a system PhoneStateListener
to provide custom UI or perform other actions during this period.
- Adds new public TelephonyManager API: notifyCarrierNetworkChange(boolean)
- Adds new @hide PhoneStateListener method: onCarrierNetworkChange(boolean)
- Functionality merely serves as a pass-through of data from an app to a
PhoneStateListener (SystemUI for the intended use case)
- Protected by MODIFY_PHONE_STATE permission or hasCarrierPrivileges().
Bug: 11392659
Change-Id: I3199e21ec1ac124198f44b86c1534dd3ff1f6858
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index d153233..908ee22 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -181,6 +181,8 @@
private PreciseCallState mPreciseCallState = new PreciseCallState();
+ private boolean mCarrierNetworkChangeState = false;
+
private PreciseDataConnectionState mPreciseDataConnectionState =
new PreciseDataConnectionState();
@@ -607,6 +609,13 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) {
+ try {
+ r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
} else {
@@ -790,6 +799,31 @@
broadcastSignalStrengthChanged(signalStrength, subId);
}
+ @Override
+ public void notifyCarrierNetworkChange(boolean active) {
+ if (!checkNotifyPermissionOrCarrierPrivilege("notifyCarrierNetworkChange()")) {
+ return;
+ }
+ if (VDBG) {
+ log("notifyCarrierNetworkChange: active=" + active);
+ }
+
+ synchronized (mRecords) {
+ mCarrierNetworkChangeState = active;
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE)) {
+ try {
+ r.callback.onCarrierNetworkChange(active);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
public void notifyCellInfo(List<CellInfo> cellInfo) {
notifyCellInfoForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellInfo);
}
@@ -1422,9 +1456,19 @@
android.Manifest.permission.READ_PRECISE_PHONE_STATE);
}
+ private boolean checkNotifyPermissionOrCarrierPrivilege(String method) {
+ if (checkNotifyPermission() || checkCarrierPrivilege()) {
+ return true;
+ }
+
+ String msg = "Modify Phone State or Carrier Privilege Permission Denial: " + method
+ + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
+ if (DBG) log(msg);
+ return false;
+ }
+
private boolean checkNotifyPermission(String method) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- == PackageManager.PERMISSION_GRANTED) {
+ if (checkNotifyPermission()) {
return true;
}
String msg = "Modify Phone State Permission Denial: " + method + " from pid="
@@ -1433,6 +1477,24 @@
return false;
}
+ private boolean checkNotifyPermission() {
+ return mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean checkCarrierPrivilege() {
+ TelephonyManager tm = TelephonyManager.getDefault();
+ String[] pkgs = mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
+ for (String pkg : pkgs) {
+ if (tm.checkCarrierPrivilegesForPackage(pkg) ==
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private void checkListenerPermission(int events) {
if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
mContext.enforceCallingOrSelfPermission(