Implement ImsManager API for Telephony and update testapps

Test: ImsTestApp
Merged-In: I0b766c549a74f192b676497b83bfb83a478cd54d
Change-Id: I0b766c549a74f192b676497b83bfb83a478cd54d
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index e38441f..4e47bda 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -79,10 +79,12 @@
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.telephony.cdma.CdmaCellLocation;
 import android.telephony.gsm.GsmCellLocation;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -90,6 +92,7 @@
 import android.util.Pair;
 import android.util.Slog;
 
+import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CallManager;
@@ -2344,7 +2347,7 @@
 
     @Override
     public VisualVoicemailSmsFilterSettings getActiveVisualVoicemailSmsFilterSettings(int subId) {
-        enforceReadPrivilegedPermission();
+        enforceReadPrivilegedPermission("getActiveVisualVoicemailSmsFilterSettings");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -2424,7 +2427,7 @@
      */
     @Override
     public int getVoiceActivationState(int subId, String callingPackage) {
-        enforceReadPrivilegedPermission();
+        enforceReadPrivilegedPermission("getVoiceActivationState");
 
         final Phone phone = getPhone(subId);
         final long identity = Binder.clearCallingIdentity();
@@ -2444,7 +2447,7 @@
      */
     @Override
     public int getDataActivationState(int subId, String callingPackage) {
-        enforceReadPrivilegedPermission();
+        enforceReadPrivilegedPermission("getDataActivationState");
 
         final Phone phone = getPhone(subId);
         final long identity = Binder.clearCallingIdentity();
@@ -2551,6 +2554,315 @@
         return (int) sendRequest(CMD_GET_NETWORK_SELECTION_MODE, null /* argument */, subId);
     }
 
+    @Override
+    public void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+            String callingPackage) throws RemoteException {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
+                "addImsRegistrationCallback")) {
+            return;
+        }
+        // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+        final long token = Binder.clearCallingIdentity();
+        try {
+            ImsManager.getInstance(mPhone.getContext(), getSlotIndexOrException(subId))
+                    .addRegistrationCallbackForSubscription(c, subId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+            String callingPackage) {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
+                "removeImsRegistrationCallback")) {
+            return;
+        }
+        // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+        Binder.withCleanCallingIdentity(() ->
+                ImsManager.getInstance(mPhone.getContext(), getSlotIndexOrException(subId))
+                        .removeRegistrationCallbackForSubscription(c, subId));
+    }
+
+    @Override
+    public void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+            String callingPackage) throws RemoteException {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
+                "addMmTelCapabilityCallback")) {
+            return;
+        }
+        // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+        final long token = Binder.clearCallingIdentity();
+        try {
+            ImsManager.getInstance(mPhone.getContext(), getSlotIndexOrException(subId))
+                    .addCapabilitiesCallbackForSubscription(c, subId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+            String callingPackage) {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
+                "removeMmTelCapabilityCallback")) {
+            return;
+        }
+        // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+        Binder.withCleanCallingIdentity(() ->
+                ImsManager.getInstance(mPhone.getContext(), getSlotIndexOrException(subId))
+                        .removeCapabilitiesCallbackForSubscription(c, subId));
+    }
+
+    @Override
+    public boolean isCapable(int subId, int capability, int regTech, String callingPackage) {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
+                "isCapable")) {
+            return false;
+        }
+        // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).queryMmTelCapability(capability, regTech);
+        } catch (ImsException e) {
+            Log.w(LOG_TAG, "IMS isCapable - service unavailable: " + e.getMessage());
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public boolean isAvailable(int subId, int capability, int regTech, String callingPackage) {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
+                "isAvailable")) {
+            return false;
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Phone phone = getPhone(subId);
+            if (phone == null) return false;
+            return phone.isImsCapabilityAvailable(capability, regTech);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public boolean isAdvancedCallingSettingEnabled(int subId) {
+        enforceReadPrivilegedPermission("enforceReadPrivilegedPermission");
+        // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).isEnhanced4gLteModeSettingEnabledByUser();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void setAdvancedCallingSetting(int subId, boolean isEnabled) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setAdvancedCallingSetting");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).setEnhanced4gLteModeSetting(isEnabled);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public boolean isVtSettingEnabled(int subId, String callingPackage) {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
+                "isVtSettingEnabled")) {
+            return false;
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).isVtEnabledByUser();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void setVtSetting(int subId, boolean isEnabled) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setVtSetting");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).setVtSetting(isEnabled);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public boolean isVoWiFiSettingEnabled(int subId) {
+        enforceReadPrivilegedPermission("isVoWiFiSettingEnabled");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).isWfcEnabledByUser();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void setVoWiFiSetting(int subId, boolean isEnabled) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setVoWiFiSetting");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).setWfcSetting(isEnabled);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public boolean isVoWiFiRoamingSettingEnabled(int subId) {
+        enforceReadPrivilegedPermission("isVoWiFiRoamingSettingEnabled");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).isWfcRoamingEnabledByUser();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void setVoWiFiRoamingSetting(int subId, boolean isEnabled) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setVoWiFiRoamingSetting");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).setWfcRoamingSetting(isEnabled);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void setVoWiFiNonPersistent(int subId, boolean isCapable, int mode) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setVoWiFiNonPersistent");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).setWfcNonPersistent(isCapable, mode);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public int getVoWiFiModeSetting(int subId) {
+        enforceReadPrivilegedPermission("getVoWiFiModeSetting");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).getWfcMode(false /*isRoaming*/);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void setVoWiFiModeSetting(int subId, int mode) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setVoWiFiModeSetting");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).setWfcMode(mode, false /*isRoaming*/);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public int getVoWiFiRoamingModeSetting(int subId) {
+        enforceReadPrivilegedPermission("getVoWiFiRoamingModeSetting");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).getWfcMode(true /*isRoaming*/);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void setVoWiFiRoamingModeSetting(int subId, int mode) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setVoWiFiRoamingModeSetting");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).setWfcMode(mode, true /*isRoaming*/);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void setRttCapabilitySetting(int subId, boolean isEnabled) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setRttCapabilityEnabled");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).setRttEnabled(isEnabled);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public boolean isTtyOverVolteEnabled(int subId) {
+        enforceReadPrivilegedPermission("isTtyOverVolteEnabled");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mPhone.getContext(),
+                    getSlotIndexOrException(subId)).isTtyOnVoLteCapable();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private int getSlotIndexOrException(int subId) throws IllegalArgumentException {
+        int slotId = SubscriptionManager.getSlotIndex(subId);
+        if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+            throw new IllegalArgumentException("Invalid Subscription Id.");
+        }
+        return slotId;
+    }
+
     /**
      * Returns the network type for a subId
      */
@@ -4153,23 +4465,6 @@
     }
 
     /**
-     * @return the VoLTE availability.
-     */
-    public boolean isVolteAvailable(int subId) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            Phone phone = getPhone(subId);
-            if (phone != null) {
-                return phone.isVolteEnabled();
-            } else {
-                return false;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
      * @return the VT calling availability.
      */
     public boolean isVideoTelephonyAvailable(int subId) {
@@ -4522,9 +4817,9 @@
      *
      * @throws SecurityException if the caller does not have the required permission
      */
-    private void enforceReadPrivilegedPermission() {
+    private void enforceReadPrivilegedPermission(String message) {
         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
-                null);
+                message);
     }
 
     /**
@@ -4570,7 +4865,7 @@
      */
     @Override
     public String getAidForAppType(int subId, int appType) {
-        enforceReadPrivilegedPermission();
+        enforceReadPrivilegedPermission("getAidForAppType");
         Phone phone = getPhone(subId);
 
         final long identity = Binder.clearCallingIdentity();
@@ -4599,7 +4894,7 @@
      */
     @Override
     public String getEsn(int subId) {
-        enforceReadPrivilegedPermission();
+        enforceReadPrivilegedPermission("getEsn");
         Phone phone = getPhone(subId);
 
         final long identity = Binder.clearCallingIdentity();
@@ -4627,7 +4922,7 @@
      */
     @Override
     public String getCdmaPrlVersion(int subId) {
-        enforceReadPrivilegedPermission();
+        enforceReadPrivilegedPermission("getCdmaPrlVersion");
         Phone phone = getPhone(subId);
 
         final long identity = Binder.clearCallingIdentity();
@@ -4702,7 +4997,7 @@
      */
     @Override
     public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) {
-        enforceReadPrivilegedPermission();
+        enforceReadPrivilegedPermission("getAllowedCarriers");
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         final long identity = Binder.clearCallingIdentity();
@@ -4945,7 +5240,7 @@
      */
     @Override
     public boolean getEmergencyCallbackMode(int subId) {
-        enforceReadPrivilegedPermission();
+        enforceReadPrivilegedPermission("getEmergencyCallbackMode");
         final Phone phone = getPhone(subId);
 
         final long identity = Binder.clearCallingIdentity();
@@ -5083,7 +5378,7 @@
 
     @Override
     public UiccSlotInfo[] getUiccSlotsInfo() {
-        enforceReadPrivilegedPermission();
+        enforceReadPrivilegedPermission("getUiccSlotsInfo");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -5256,7 +5551,7 @@
 
     @Override
     public int getCarrierIdListVersion(int subId) {
-        enforceReadPrivilegedPermission();
+        enforceReadPrivilegedPermission("getCarrierIdListVersion");
 
         final long identity = Binder.clearCallingIdentity();
         try {
diff --git a/testapps/ImsTestService/AndroidManifest.xml b/testapps/ImsTestService/AndroidManifest.xml
index f47210e..7662a42 100644
--- a/testapps/ImsTestService/AndroidManifest.xml
+++ b/testapps/ImsTestService/AndroidManifest.xml
@@ -18,6 +18,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           coreApp="true"
           package="com.android.phone.testapps.imstestapp">
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <application
         android:label="ImsTestService"
         android:directBootAware="true">
diff --git a/testapps/ImsTestService/res/layout/activity_calling.xml b/testapps/ImsTestService/res/layout/activity_calling.xml
index 01cdbb1..626f6dc 100644
--- a/testapps/ImsTestService/res/layout/activity_calling.xml
+++ b/testapps/ImsTestService/res/layout/activity_calling.xml
@@ -73,5 +73,22 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content" android:text=""/>
 
+    <View
+        android:layout_width="match_parent"
+        android:layout_height= "1dp"
+        android:paddingRight="4dp"
+        android:background="?android:attr/listDivider" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textStyle="bold"
+        android:text="Capability Changed Events:"/>
+
+    <ListView
+        android:id="@+id/cap_cb_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
 
 </LinearLayout>
\ No newline at end of file
diff --git a/testapps/ImsTestService/res/layout/activity_registration.xml b/testapps/ImsTestService/res/layout/activity_registration.xml
index 2e381eb..cb6c10e 100644
--- a/testapps/ImsTestService/res/layout/activity_registration.xml
+++ b/testapps/ImsTestService/res/layout/activity_registration.xml
@@ -75,4 +75,21 @@
         android:layout_height="wrap_content"
         android:text="@string/reg_changefailed_button"
         android:id="@+id/reg_changefailed_button"/>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height= "1dp"
+        android:paddingRight="4dp"
+        android:background="?android:attr/listDivider" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textStyle="bold"
+        android:text="Registration Callback Listener Events:"/>
+
+    <ListView
+        android:id="@+id/reg_cb_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java
index 58e08cc..ae5b39e 100644
--- a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java
@@ -17,17 +17,103 @@
 package com.android.phone.testapps.imstestapp;
 
 import android.app.Activity;
+import android.content.Context;
 import android.os.Bundle;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
 import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.CheckBox;
+import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
 
+import java.util.ArrayList;
+import java.util.Objects;
+
 public class ImsCallingActivity extends Activity {
 
+    private static final String PREFIX_ITEM = "Capability Event: ";
+    private static final String PREFIX_VALUE = "Value: ";
+
+    private static class CapItem {
+        public String key;
+        public String value;
+
+        CapItem(String key, String value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        CapItem(String key, int value) {
+            this.key = key;
+            this.value = String.valueOf(value);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            CapItem regItem = (CapItem) o;
+            return Objects.equals(key, regItem.key)
+                    && Objects.equals(value, regItem.value);
+        }
+
+        @Override
+        public int hashCode() {
+
+            return Objects.hash(key, value);
+        }
+    }
+
+    private static class CapItemAdapter extends ArrayAdapter<CapItem> {
+        CapItemAdapter(Context context, ArrayList<CapItem> regItems) {
+            super(context, 0, regItems);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            CapItem capItem = getItem(position);
+
+            if (convertView == null) {
+                convertView = LayoutInflater.from(getContext()).inflate(R.layout.config_item,
+                        parent, false);
+            }
+
+            TextView textItem = (TextView) convertView.findViewById(R.id.configItem);
+            TextView textValue = (TextView) convertView.findViewById(R.id.configValue);
+
+            textItem.setText(PREFIX_ITEM + capItem.key);
+            textValue.setText(PREFIX_VALUE + capItem.value);
+
+            return convertView;
+        }
+    }
+
+    private final ImsMmTelManager.CapabilityCallback mCapabilityCallback =
+            new ImsMmTelManager.CapabilityCallback() {
+
+        @Override
+        public void onCapabilitiesStatusChanged(
+                MmTelFeature.MmTelCapabilities capabilities) {
+            Log.i("ImsCallingActivity" , "onCapabilitiesStatusChanged:" + capabilities);
+            mCapabilityEvents.add(new CapItem("cap changed: ", capabilities.toString()));
+            notifyDataChanged();
+
+        }
+
+        private void notifyDataChanged() {
+            mCapabiltyEventAdapter.notifyDataSetChanged();
+        }
+    };
+
     //Capabilities available by service
     private CheckBox mCapVoiceAvailBox;
     private CheckBox mCapVideoAvailBox;
@@ -36,6 +122,12 @@
 
     private TextView mCapEnabledText;
 
+    private ArrayList<CapItem> mCapabilityEvents = new ArrayList<>();
+    private CapItemAdapter mCapabiltyEventAdapter;
+    private ListView mListView;
+
+    private ImsMmTelManager mImsManager;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -65,6 +157,25 @@
     protected void onResume() {
         super.onResume();
         mmTelCapabilityChanged();
+
+        mCapabiltyEventAdapter = new CapItemAdapter(this, mCapabilityEvents);
+        mListView = (ListView) findViewById(R.id.cap_cb_list);
+        mListView.setAdapter(mCapabiltyEventAdapter);
+        try {
+            mImsManager = ImsMmTelManager.createForSubscriptionId(this,
+                    SubscriptionManager.getDefaultVoiceSubscriptionId());
+            Log.i("ImsCallingActivity", "onResume");
+            mImsManager.addMmTelCapabilityCallback(getMainExecutor(), mCapabilityCallback);
+        } catch (IllegalArgumentException e) {
+            Log.w("ImsCallingActivity", "illegal subscription ID.");
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mImsManager.removeMmTelCapabilityCallback(mCapabilityCallback);
+        mImsManager = null;
     }
 
     private void mmTelCapabilityChanged() {
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
index 43dd75a..bca8eba 100644
--- a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
@@ -17,22 +17,128 @@
 package com.android.phone.testapps.imstestapp;
 
 import android.app.Activity;
+import android.content.Context;
 import android.os.Bundle;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.ArrayMap;
+import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.EditText;
+import android.widget.ListView;
 import android.widget.Spinner;
+import android.widget.TextView;
 import android.widget.Toast;
 
+import java.util.ArrayList;
 import java.util.Map;
+import java.util.Objects;
 
 public class ImsRegistrationActivity extends Activity {
 
+    private static final String PREFIX_ITEM = "Registration Event: ";
+    private static final String PREFIX_VALUE = "Value: ";
+
+
+    private static class RegItem {
+        public String key;
+        public String value;
+
+        RegItem(String key, int value) {
+            this.key = key;
+            this.value = String.valueOf(value);
+        }
+
+        RegItem(String key, String value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            RegItem regItem = (RegItem) o;
+            return Objects.equals(key, regItem.key)
+                    && Objects.equals(value, regItem.value);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(key, value);
+        }
+    }
+
+    private static class RegItemAdapter extends ArrayAdapter<RegItem> {
+        RegItemAdapter(Context context, ArrayList<RegItem> regItems) {
+            super(context, 0, regItems);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            RegItem regItem = getItem(position);
+
+            if (convertView == null) {
+                convertView = LayoutInflater.from(getContext()).inflate(R.layout.config_item,
+                        parent, false);
+            }
+
+            TextView textItem = (TextView) convertView.findViewById(R.id.configItem);
+            TextView textValue = (TextView) convertView.findViewById(R.id.configValue);
+
+            textItem.setText(PREFIX_ITEM + regItem.key);
+            textValue.setText(PREFIX_VALUE + regItem.value);
+
+            return convertView;
+        }
+    }
+
+
+    private final ImsMmTelManager.RegistrationCallback mRegistrationCallback =
+            new ImsMmTelManager.RegistrationCallback() {
+
+        @Override
+        public void onRegistered(int imsRadioTech) {
+            Log.i("ImsRegistrationActivity", "onRegistered: " + imsRadioTech);
+            mRegItems.add(new RegItem("Registered", REG_TECH_STRING.get(imsRadioTech)));
+            triggerAdapterChange();
+        }
+
+        @Override
+        public void onRegistering(int imsRadioTech) {
+            Log.i("ImsRegistrationActivity", "onRegistering: " + imsRadioTech);
+            mRegItems.add(new RegItem("Registering", REG_TECH_STRING.get(imsRadioTech)));
+            triggerAdapterChange();
+        }
+
+        @Override
+        public void onDeregistered(ImsReasonInfo info) {
+            Log.i("ImsRegistrationActivity", "onDeregistered: " + info);
+            mRegItems.add(new RegItem("Deregistered", info.toString()));
+            triggerAdapterChange();
+        }
+
+        @Override
+        public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
+            mRegItems.add(new RegItem("TechnologyChangeFailed", REG_TECH_STRING.get(imsRadioTech)
+                    + " reason: " + info));
+            triggerAdapterChange();
+        }
+
+        private void triggerAdapterChange() {
+            mRegItemAdapter.notifyDataSetChanged();
+        }
+    };
+
+
+
     private int mSelectedRegTech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
 
     private static final Map<String, Integer> REG_TECH = new ArrayMap<>(2);
@@ -40,15 +146,43 @@
         REG_TECH.put("LTE", ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
         REG_TECH.put("IWLAN", ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
     }
+    private static final Map<Integer, String> REG_TECH_STRING = new ArrayMap<>(2);
+    static {
+        REG_TECH_STRING.put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, "NONE");
+        REG_TECH_STRING.put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, "LTE");
+        REG_TECH_STRING.put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, "IWLAN");
+    }
+
+
+
+    private ArrayList<RegItem> mRegItems = new ArrayList<>();
+    RegItemAdapter mRegItemAdapter;
+    ListView mListView;
 
     private View mDeregisteredReason;
     private View mRegChangeFailedReason;
+    private ImsMmTelManager mImsManager;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.activity_registration);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mRegItemAdapter = new RegItemAdapter(this, mRegItems);
+        mListView = (ListView) findViewById(R.id.reg_cb_list);
+        mListView.setAdapter(mRegItemAdapter);
+        try {
+            mImsManager = ImsMmTelManager.createForSubscriptionId(this,
+                    SubscriptionManager.getDefaultVoiceSubscriptionId());
+            mImsManager.addImsRegistrationCallback(getMainExecutor(), mRegistrationCallback);
+        } catch (IllegalArgumentException e) {
+            Log.w("ImsCallingActivity", "illegal subscription ID.");
+        }
 
         //Set up registration tech spinner
         Spinner regTechDropDown = findViewById(R.id.reg_tech_selector);
@@ -82,6 +216,13 @@
         mRegChangeFailedReason = findViewById(R.id.regchangefail_imsreasoninfo);
     }
 
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mImsManager.removeImsRegistrationCallback(mRegistrationCallback);
+        mImsManager = null;
+    }
+
     private void onRegisteredClicked() {
         if (!isFrameworkConnected()) {
             return;