Refactored cell broadcast area info support

Area info cell broadcast messages are not handled
by cell broadcast receiver anymore. Instead, they will
be processed in cell broadcast service. Privileged apps
that are interested in getting the information will need
to get it directly from cell broadcast service.

Note this is only enabled for certain countries and carriers.

Test: Manual
Bug: 141318859
Change-Id: If6a0a0c6a76e8b90a8feaea1e2771993e2d0edfe
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ac1e440..3cabb0e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -102,6 +102,7 @@
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
     <uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
+    <uses-permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE" />
 
     <application android:label="@string/settings_label"
             android:icon="@drawable/ic_launcher_settings"
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
index 6abd52d..1986965 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
@@ -16,22 +16,26 @@
 
 package com.android.settings.deviceinfo.simstatus;
 
-import android.Manifest;
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.content.res.Resources;
-import android.os.Bundle;
+import android.os.IBinder;
 import android.os.PersistableBundle;
-import android.os.UserHandle;
+import android.os.RemoteException;
 import android.telephony.Annotation;
 import android.telephony.CarrierConfigManager;
+import android.telephony.CellBroadcastIntents;
+import android.telephony.CellBroadcastService;
 import android.telephony.CellSignalStrength;
+import android.telephony.ICellBroadcastService;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
-import android.telephony.SmsCbMessage;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
@@ -39,6 +43,7 @@
 import android.telephony.UiccCardInfo;
 import android.telephony.euicc.EuiccManager;
 import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
@@ -58,6 +63,8 @@
 
     private final static String TAG = "SimStatusDialogCtrl";
 
+    private static final String CELL_BROADCAST_SERVICE_PACKAGE = "com.android.cellbroadcastservice";
+
     @VisibleForTesting
     final static int NETWORK_PROVIDER_VALUE_ID = R.id.operator_name_value;
     @VisibleForTesting
@@ -96,12 +103,6 @@
     @VisibleForTesting
     static final int MAX_PHONE_COUNT_SINGLE_SIM = 1;
 
-    private final static String CB_AREA_INFO_RECEIVED_ACTION =
-            "com.android.cellbroadcastreceiver.CB_AREA_INFO_RECEIVED";
-    private final static String GET_LATEST_CB_AREA_INFO_ACTION =
-            "com.android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO";
-    private final static String CELL_BROADCAST_RECEIVER_APP = "com.android.cellbroadcastreceiver";
-
     private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
             new OnSubscriptionsChangedListener() {
                 @Override
@@ -131,23 +132,48 @@
     private final BroadcastReceiver mAreaInfoReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (TextUtils.equals(action, CB_AREA_INFO_RECEIVED_ACTION)) {
-                final Bundle extras = intent.getExtras();
-                if (extras == null) {
-                    return;
-                }
-                final SmsCbMessage cbMessage = (SmsCbMessage) extras.get("message");
-                if (cbMessage != null && mSlotIndex == cbMessage.getSlotIndex()) {
-                    final String latestAreaInfo = cbMessage.getMessageBody();
-                    mDialog.setText(OPERATOR_INFO_VALUE_ID, latestAreaInfo);
-                }
-            }
+            updateAreaInfoText();
         }
     };
 
     private PhoneStateListener mPhoneStateListener;
 
+    private CellBroadcastServiceConnection mCellBroadcastServiceConnection;
+
+    private class CellBroadcastServiceConnection implements ServiceConnection {
+        private IBinder mService;
+
+        @Nullable
+        public IBinder getService() {
+            return mService;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            Log.d(TAG, "connected to CellBroadcastService");
+            this.mService = service;
+            updateAreaInfoText();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            this.mService = null;
+            Log.d(TAG, "mICellBroadcastService has disconnected unexpectedly");
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            this.mService = null;
+            Log.d(TAG, "Binding died");
+        }
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            this.mService = null;
+            Log.d(TAG, "Null binding");
+        }
+    }
+
     public SimStatusDialogController(@NonNull SimStatusDialogFragment dialog, Lifecycle lifecycle,
             int slotId) {
         mDialog = dialog;
@@ -190,6 +216,19 @@
         updateImsRegistrationState();
     }
 
+    /**
+     * Deinitialization works
+     */
+    public void deinitialize() {
+        if (mShowLatestAreaInfo) {
+            if (mCellBroadcastServiceConnection != null
+                    && mCellBroadcastServiceConnection.getService() != null) {
+                mContext.unbindService(mCellBroadcastServiceConnection);
+            }
+            mCellBroadcastServiceConnection = null;
+        }
+    }
+
     @Override
     public void onResume() {
         if (mSubscriptionInfo == null) {
@@ -204,14 +243,9 @@
         mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
 
         if (mShowLatestAreaInfo) {
+            updateAreaInfoText();
             mContext.registerReceiver(mAreaInfoReceiver,
-                    new IntentFilter(CB_AREA_INFO_RECEIVED_ACTION),
-                    Manifest.permission.RECEIVE_EMERGENCY_BROADCAST, null /* scheduler */);
-            // Ask CellBroadcastReceiver to broadcast the latest area info received
-            final Intent getLatestIntent = new Intent(GET_LATEST_CB_AREA_INFO_ACTION);
-            getLatestIntent.setPackage(CELL_BROADCAST_RECEIVER_APP);
-            mContext.sendBroadcastAsUser(getLatestIntent, UserHandle.ALL,
-                    Manifest.permission.RECEIVE_EMERGENCY_BROADCAST);
+                    new IntentFilter(CellBroadcastIntents.ACTION_AREA_INFO_UPDATED));
         }
     }
 
@@ -265,13 +299,54 @@
         mDialog.setText(CELLULAR_NETWORK_STATE, networkStateValue);
     }
 
+    /**
+     * Update area info text retrieved from
+     * {@link CellBroadcastService#getCellBroadcastAreaInfo(int)}
+     */
+    private void updateAreaInfoText() {
+        if (!mShowLatestAreaInfo || mCellBroadcastServiceConnection == null) return;
+        ICellBroadcastService cellBroadcastService =
+                ICellBroadcastService.Stub.asInterface(
+                        mCellBroadcastServiceConnection.getService());
+        if (cellBroadcastService == null) return;
+        try {
+            mDialog.setText(OPERATOR_INFO_VALUE_ID,
+                    cellBroadcastService.getCellBroadcastAreaInfo(
+                            SimStatusDialogController.this.mSlotIndex));
+
+        } catch (RemoteException e) {
+            Log.d(TAG, "Can't get area info. e=" + e);
+        }
+    }
+
+    /**
+     * Bind cell broadcast service.
+     */
+    private void bindCellBroadcastService() {
+        mCellBroadcastServiceConnection = new CellBroadcastServiceConnection();
+        Intent intent = new Intent(CellBroadcastService.CELL_BROADCAST_SERVICE_INTERFACE);
+        intent.setPackage(CELL_BROADCAST_SERVICE_PACKAGE);
+        if (mCellBroadcastServiceConnection != null
+                && mCellBroadcastServiceConnection.getService() == null) {
+            if (!mContext.bindService(intent, mCellBroadcastServiceConnection,
+                    Context.BIND_AUTO_CREATE)) {
+                Log.e(TAG, "Unable to bind to service");
+            }
+        } else {
+            Log.d(TAG, "skipping bindService because connection already exists");
+        }
+    }
 
     private void updateLatestAreaInfo() {
         mShowLatestAreaInfo = Resources.getSystem().getBoolean(
                 com.android.internal.R.bool.config_showAreaUpdateInfoSettings)
                 && mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA;
 
-        if (!mShowLatestAreaInfo) {
+        if (mShowLatestAreaInfo) {
+            // Bind cell broadcast service to get the area info. The info will be updated once
+            // the service is connected.
+            bindCellBroadcastService();
+        } else {
             mDialog.removeSettingFromScreen(OPERATOR_INFO_LABEL_ID);
             mDialog.removeSettingFromScreen(OPERATOR_INFO_VALUE_ID);
         }
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
index d2d563f..93323b3 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
@@ -74,6 +74,12 @@
         return builder.setView(mRootView).create();
     }
 
+    @Override
+    public void onDestroy() {
+        mController.deinitialize();
+        super.onDestroy();
+    }
+
     public void removeSettingFromScreen(int viewId) {
         final View view = mRootView.findViewById(viewId);
         if (view != null) {