Merge "DO NOT MERGE - Merge QQ1A.200205.002 into master"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7ca32f4..1b7b082 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -102,6 +102,7 @@
     <uses-permission android:name="android.permission.MANAGE_SCOPED_ACCESS_DIRECTORY_PERMISSIONS" />
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
+    <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/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 749a38f..aa12409 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -109,7 +109,7 @@
             profilePref.setChecked(profile.getConnectionStatus(device) ==
                     BluetoothProfile.STATE_CONNECTED);
         } else {
-            profilePref.setChecked(profile.isPreferred(device));
+            profilePref.setChecked(profile.isEnabled(device));
         }
 
         if (profile instanceof A2dpProfile) {
@@ -117,7 +117,7 @@
             SwitchPreference highQualityPref = (SwitchPreference) mProfilesContainer.findPreference(
                     HIGH_QUALITY_AUDIO_PREF_TAG);
             if (highQualityPref != null) {
-                if (a2dp.isPreferred(device) && a2dp.supportsHighQualityAudio(device)) {
+                if (a2dp.isEnabled(device) && a2dp.supportsHighQualityAudio(device)) {
                     highQualityPref.setVisible(true);
                     highQualityPref.setTitle(a2dp.getHighQualityAudioOptionLabel(device));
                     highQualityPref.setChecked(a2dp.isHighQualityAudioEnabled(device));
@@ -142,8 +142,7 @@
         if (profile instanceof MapProfile) {
             bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
         }
-        profile.setPreferred(bluetoothDevice, true);
-        mCachedDevice.connectProfile(profile);
+        profile.setEnabled(bluetoothDevice, true);
     }
 
     /**
@@ -151,8 +150,7 @@
      */
     private void disableProfile(LocalBluetoothProfile profile) {
         final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
-        mCachedDevice.disconnect(profile);
-        profile.setPreferred(bluetoothDevice, false);
+        profile.setEnabled(bluetoothDevice, false);
         if (profile instanceof MapProfile) {
             bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
         } else if (profile instanceof PbapServerProfile) {
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
index 26d8e2b..04112c4 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;
@@ -59,6 +64,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
@@ -94,12 +101,6 @@
     @VisibleForTesting
     final static int IMS_REGISTRATION_STATE_VALUE_ID = R.id.ims_reg_state_value;
 
-    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
@@ -129,23 +130,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;
@@ -188,6 +214,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) {
@@ -202,14 +241,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));
         }
     }
 
@@ -263,13 +297,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) {
diff --git a/src/com/android/settings/security/SimLockPreferenceController.java b/src/com/android/settings/security/SimLockPreferenceController.java
index d4def6b..3b23daa 100644
--- a/src/com/android/settings/security/SimLockPreferenceController.java
+++ b/src/com/android/settings/security/SimLockPreferenceController.java
@@ -38,7 +38,7 @@
     private final CarrierConfigManager mCarrierConfigManager;
     private final UserManager mUserManager;
     private final SubscriptionManager mSubscriptionManager;
-    private final TelephonyManager mTelephonyManager;
+    private TelephonyManager mTelephonyManager;
 
     public SimLockPreferenceController(Context context) {
         super(context, KEY_SIM_LOCK);
@@ -99,7 +99,9 @@
 
         if (subInfoList != null) {
             for (SubscriptionInfo subInfo : subInfoList) {
-                if (mTelephonyManager.hasIccCard(subInfo.getSimSlotIndex())) {
+                mTelephonyManager = mTelephonyManager
+                        .createForSubscriptionId(subInfo.getSimSlotIndex());
+                if (mTelephonyManager.hasIccCard()) {
                     return true;
                 }
             }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
index 9637884..5fc45a6 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
@@ -115,18 +115,6 @@
         }
 
         @Override
-        public boolean connect(BluetoothDevice device) {
-            mConnectedDevices.add(device);
-            return true;
-        }
-
-        @Override
-        public boolean disconnect(BluetoothDevice device) {
-            mConnectedDevices.remove(device);
-            return false;
-        }
-
-        @Override
         public int getConnectionStatus(BluetoothDevice device) {
             if (mConnectedDevices.contains(device)) {
                 return BluetoothProfile.STATE_CONNECTED;
@@ -136,20 +124,21 @@
         }
 
         @Override
-        public boolean isPreferred(BluetoothDevice device) {
+        public boolean isEnabled(BluetoothDevice device) {
             return mPreferred.getOrDefault(device, false);
         }
 
         @Override
-        public int getPreferred(BluetoothDevice device) {
-            return isPreferred(device) ?
-                    BluetoothProfile.CONNECTION_POLICY_ALLOWED
+        public int getConnectionPolicy(BluetoothDevice device) {
+            return isEnabled(device)
+                    ? BluetoothProfile.CONNECTION_POLICY_ALLOWED
                     : BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
         }
 
         @Override
-        public void setPreferred(BluetoothDevice device, boolean preferred) {
-            mPreferred.put(device, preferred);
+        public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+            mPreferred.put(device, enabled);
+            return true;
         }
 
         @Override
@@ -192,7 +181,7 @@
     private LocalBluetoothProfile addFakeProfile(int profileNameResId,
             boolean deviceIsPreferred) {
         LocalBluetoothProfile profile = new FakeBluetoothProfile(mContext, profileNameResId);
-        profile.setPreferred(mDevice, deviceIsPreferred);
+        profile.setEnabled(mDevice, deviceIsPreferred);
         mConnectableProfiles.add(profile);
         when(mProfileManager.getProfileByName(eq(profile.toString()))).thenReturn(profile);
         return profile;
@@ -266,7 +255,7 @@
         assertThat(pref.isChecked()).isTrue();
         pref.performClick();
         assertThat(pref.isChecked()).isFalse();
-        assertThat(mConnectableProfiles.get(0).isPreferred(mDevice)).isFalse();
+        assertThat(mConnectableProfiles.get(0).isEnabled(mDevice)).isFalse();
 
         // Make sure no new preferences were added.
         assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
@@ -274,7 +263,7 @@
         // Clicking the pref again should make the profile once again preferred.
         pref.performClick();
         assertThat(pref.isChecked()).isTrue();
-        assertThat(mConnectableProfiles.get(0).isPreferred(mDevice)).isTrue();
+        assertThat(mConnectableProfiles.get(0).isEnabled(mDevice)).isTrue();
 
         // Make sure we still haven't gotten any new preferences added.
         assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
@@ -364,7 +353,7 @@
             mContext.getString(R.string.bluetooth_profile_a2dp_high_quality_unknown_codec));
         when(profile.supportsHighQualityAudio(mDevice)).thenReturn(supportsHighQualityAudio);
         when(profile.isHighQualityAudioEnabled(mDevice)).thenReturn(highQualityAudioEnabled);
-        when(profile.isPreferred(mDevice)).thenReturn(preferred);
+        when(profile.isEnabled(mDevice)).thenReturn(preferred);
         when(profile.isProfileReady()).thenReturn(true);
         mConnectableProfiles.add(profile);
         return profile;
@@ -426,8 +415,8 @@
         SwitchPreference audioPref =
             (SwitchPreference) mScreen.findPreference(audioProfile.toString());
         audioPref.performClick();
-        verify(audioProfile).setPreferred(mDevice, false);
-        when(audioProfile.isPreferred(mDevice)).thenReturn(false);
+        verify(audioProfile).setEnabled(mDevice, false);
+        when(audioProfile.isEnabled(mDevice)).thenReturn(false);
         mController.onDeviceAttributesChanged();
         assertThat(audioPref.isVisible()).isTrue();
         SwitchPreference highQualityAudioPref = getHighQualityAudioPref();
@@ -435,8 +424,8 @@
 
         // And re-enabling media audio should make high quality switch to reappear.
         audioPref.performClick();
-        verify(audioProfile).setPreferred(mDevice, true);
-        when(audioProfile.isPreferred(mDevice)).thenReturn(true);
+        verify(audioProfile).setEnabled(mDevice, true);
+        when(audioProfile.isEnabled(mDevice)).thenReturn(true);
         mController.onDeviceAttributesChanged();
         highQualityAudioPref = getHighQualityAudioPref();
         assertThat(highQualityAudioPref.isVisible()).isTrue();
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardControllerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardControllerTest.java
index 06f9b98..ad2930d 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardControllerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardControllerTest.java
@@ -23,8 +23,11 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 
 import com.android.settings.homepage.contextualcards.ContextualCard;
 import com.android.settings.homepage.contextualcards.ContextualCard.CardType;
@@ -37,6 +40,8 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowSubscriptionManager;
+import org.robolectric.shadows.ShadowTelephonyManager;
 import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
@@ -45,6 +50,7 @@
 
 @RunWith(RobolectricTestRunner.class)
 public class ConditionContextualCardControllerTest {
+    private static final int SUB_ID = 2;
 
     @Mock
     private ConditionManager mConditionManager;
@@ -57,6 +63,17 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
+
+        // parameters required by CellularDataConditionController
+        final ShadowSubscriptionManager shadowSubscriptionMgr = shadowOf(
+                mContext.getSystemService(SubscriptionManager.class));
+        shadowSubscriptionMgr.setDefaultDataSubscriptionId(SUB_ID);
+
+        final TelephonyManager telephonyManager =
+                spy(mContext.getSystemService(TelephonyManager.class));
+        final ShadowTelephonyManager shadowTelephonyMgr = shadowOf(telephonyManager);
+        shadowTelephonyMgr.setTelephonyManagerForSubscriptionId(SUB_ID, telephonyManager);
+
         mController = spy(new ConditionContextualCardController(mContext));
         ReflectionHelpers.setField(mController, "mConditionManager", mConditionManager);
     }
diff --git a/tests/robotests/src/com/android/settings/security/SimLockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/SimLockPreferenceControllerTest.java
index 0ae33a0..ebafceb 100644
--- a/tests/robotests/src/com/android/settings/security/SimLockPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/security/SimLockPreferenceControllerTest.java
@@ -141,7 +141,8 @@
         final List<SubscriptionInfo> subscriptionInfoList = new ArrayList<>();
         SubscriptionInfo info = mock(SubscriptionInfo.class);
         subscriptionInfoList.add(info);
-        when(mTelephonyManager.hasIccCard(anyInt())).thenReturn(true);
+        when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+        when(mTelephonyManager.hasIccCard()).thenReturn(true);
         when(mSubscriptionManager.getActiveSubscriptionInfoList(eq(true)))
                 .thenReturn(subscriptionInfoList);
     }