Merge "Modify error handling for registerForNtnSignalStrength" into main
diff --git a/flags/data.aconfig b/flags/data.aconfig
index 152660b..267048e 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -83,3 +83,10 @@
   description: "notify data activity changed for slot id"
   bug: "309896936"
 }
+
+flag {
+  name: "vonr_enabled_metric"
+  namespace: "telephony"
+  description: "Collect vonr status in voice call metric"
+  bug:"288449751"
+}
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 6d1008b..d7a6062 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -280,6 +280,7 @@
     optional bool is_iwlan_cross_sim_at_start = 37;
     optional bool is_iwlan_cross_sim_at_end = 38;
     optional bool is_iwlan_cross_sim_at_connected = 39;
+    optional bool vonr_enabled = 40;
 
     // Internal use only
     optional int64 setup_begin_millis = 10001;
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 3ef7164..620b871 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -254,6 +254,7 @@
             CellBroadcastConfigTracker.make(this, null, true);
 
     private boolean mIsNullCipherAndIntegritySupported = false;
+    private boolean mIsIdentifierDisclosureTransparencySupported = false;
 
     // Create Cfu (Call forward unconditional) so that dialing number &
     // mOnComplete (Message object passed by client) can be packed &
@@ -341,7 +342,7 @@
 
         // phone type needs to be set before other initialization as other objects rely on it
         mPrecisePhoneType = precisePhoneType;
-        mVoiceCallSessionStats = new VoiceCallSessionStats(mPhoneId, this);
+        mVoiceCallSessionStats = new VoiceCallSessionStats(mPhoneId, this, featureFlags);
         mImsManagerFactory = imsManagerFactory;
         initOnce(ci);
         initRatSpecific(precisePhoneType);
@@ -3144,6 +3145,7 @@
         mCi.areUiccApplicationsEnabled(obtainMessage(EVENT_GET_UICC_APPS_ENABLEMENT_DONE));
 
         handleNullCipherEnabledChange();
+        handleIdentifierDisclosureNotificationPreferenceChange();
     }
 
     private void handleRadioOn() {
@@ -3610,14 +3612,7 @@
             case EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE:
                 logd("EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE");
                 ar = (AsyncResult) msg.obj;
-                // Only test for a success here in order to flip the support flag.
-                // Testing for the negative case, e.g. REQUEST_NOT_SUPPORTED, is insufficient
-                // because the modem or the RIL could still return exceptions for temporary
-                // failures even when the feature is unsupported.
-                if (ar == null || ar.exception == null) {
-                    mIsNullCipherAndIntegritySupported = true;
-                    return;
-                }
+                mIsNullCipherAndIntegritySupported = doesResultIndicateModemSupport(ar);
                 break;
 
             case EVENT_IMS_DEREGISTRATION_TRIGGERED:
@@ -3701,11 +3696,25 @@
                 }
                 break;
 
+            case EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE:
+                logd("EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE");
+                ar = (AsyncResult) msg.obj;
+                mIsIdentifierDisclosureTransparencySupported = doesResultIndicateModemSupport(ar);
+                break;
+
             default:
                 super.handleMessage(msg);
         }
     }
 
+    private boolean doesResultIndicateModemSupport(AsyncResult ar) {
+        // We can only say that the modem supports a call without ambiguity if there
+        // is no exception set on the response.  Testing for REQUEST_NOT_SUPPORTED, is
+        // insufficient because the modem or the RIL could still return exceptions for temporary
+        // failures even when the feature is unsupported.
+        return (ar == null || ar.exception == null);
+    }
+
     private void parseImeiInfo(Message msg) {
         AsyncResult ar = (AsyncResult)msg.obj;
         if (ar.exception != null || ar.result == null) {
@@ -5298,7 +5307,31 @@
     }
 
     @Override
+    public void handleIdentifierDisclosureNotificationPreferenceChange() {
+        if (!mFeatureFlags.enableIdentifierDisclosureTransparency()) {
+            logi("Not handling identifier disclosure preference change. Feature flag "
+                    + "ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY disabled");
+            return;
+        }
+        boolean prefEnabled = getIdentifierDisclosureNotificationsPreferenceEnabled();
+
+        if (prefEnabled) {
+            mIdentifierDisclosureNotifier.enable();
+        } else {
+            mIdentifierDisclosureNotifier.disable();
+        }
+
+        mCi.setCellularIdentifierTransparencyEnabled(prefEnabled,
+                obtainMessage(EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE));
+    }
+
+    @Override
     public boolean isNullCipherAndIntegritySupported() {
         return mIsNullCipherAndIntegritySupported;
     }
+
+    @Override
+    public boolean isIdentifierDisclosureTransparencySupported() {
+        return mIsIdentifierDisclosureTransparencySupported;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index cc1d4a4..d944738 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -255,7 +255,8 @@
     protected static final int EVENT_SET_N1_MODE_ENABLED_DONE = 70;
     protected static final int EVENT_IMEI_MAPPING_CHANGED = 71;
     protected static final int EVENT_CELL_IDENTIFIER_DISCLOSURE = 72;
-    protected static final int EVENT_LAST = EVENT_CELL_IDENTIFIER_DISCLOSURE;
+    protected static final int EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE = 73;
+    protected static final int EVENT_LAST = EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE;
 
     // For shared prefs.
     private static final String GSM_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_roaming_list_";
@@ -286,6 +287,9 @@
             "pref_null_cipher_and_integrity_enabled";
     private final TelephonyAdminReceiver m2gAdminUpdater;
 
+    public static final String PREF_IDENTIFIER_DISCLOSURE_NOTIFICATIONS_ENABLED =
+            "pref_identifier_disclosure_notifications_enabled";
+
     protected final FeatureFlags mFeatureFlags;
 
     /**
@@ -5149,6 +5153,28 @@
     }
 
     /**
+     * @return whether or not this Phone interacts with a modem that supports the cellular
+     * identifier disclosure transparency feature.
+     */
+    public boolean isIdentifierDisclosureTransparencySupported() {
+        return false;
+    }
+
+    /**
+     * @return global cellular identifier disclosure transparency enabled preference
+     */
+    public boolean getIdentifierDisclosureNotificationsPreferenceEnabled() {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+        return sp.getBoolean(PREF_IDENTIFIER_DISCLOSURE_NOTIFICATIONS_ENABLED, false);
+    }
+
+    /**
+     * Override to handle an update to the cellular identifier disclosure transparency preference.
+     */
+    public void handleIdentifierDisclosureNotificationPreferenceChange() {
+    }
+
+    /**
      * Notifies the IMS call status to the modem.
      *
      * @param imsCallInfo The list of {@link ImsCallInfo}.
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 0cbc035..d29eed1 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -161,7 +161,7 @@
                 }
 
                 // register statsd pullers.
-                sMetricsCollector = new MetricsCollector(context);
+                sMetricsCollector = new MetricsCollector(context, sFeatureFlags);
 
                 sPhoneNotifier = new DefaultPhoneNotifier(context, featureFlags);
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index e608e4d..5177adb 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -1559,7 +1559,7 @@
     public void getSystemSelectionChannels(Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
         if (!canMakeRequest("getSystemSelectionChannels", networkProxy, result,
-                RADIO_HAL_VERSION_1_4)) {
+                RADIO_HAL_VERSION_1_6)) {
             return;
         }
 
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index e27d14b..8bd2547 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -55,6 +55,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
 import static com.android.internal.telephony.util.TelephonyUtils.IS_DEBUGGABLE;
 
+import android.annotation.NonNull;
 import android.app.StatsManager;
 import android.content.Context;
 import android.telephony.SubscriptionManager;
@@ -66,6 +67,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyStatsLog;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
@@ -167,21 +169,25 @@
     private final PersistAtomsStorage mStorage;
     private final DeviceStateHelper mDeviceStateHelper;
     private final StatsManager mStatsManager;
+    private final VonrHelper mVonrHelper;
     private final AirplaneModeStats mAirplaneModeStats;
     private final Set<DataCallSessionStats> mOngoingDataCallStats = ConcurrentHashMap.newKeySet();
     private static final Random sRandom = new Random();
 
-    public MetricsCollector(Context context) {
-        this(context, new PersistAtomsStorage(context), new DeviceStateHelper(context));
+    public MetricsCollector(Context context, @NonNull FeatureFlags featureFlags) {
+        this(context, new PersistAtomsStorage(context),
+                new DeviceStateHelper(context), new VonrHelper(featureFlags));
     }
 
     /** Allows dependency injection. Used during unit tests. */
     @VisibleForTesting
     public MetricsCollector(
-            Context context, PersistAtomsStorage storage, DeviceStateHelper deviceStateHelper) {
+            Context context, PersistAtomsStorage storage, DeviceStateHelper deviceStateHelper,
+                    VonrHelper vonrHelper) {
         mStorage = storage;
         mDeviceStateHelper = deviceStateHelper;
         mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER);
+        mVonrHelper = vonrHelper;
         if (mStatsManager != null) {
             // Most (but not all) of these are subject to cooldown specified by MIN_COOLDOWN_MILLIS.
             registerAtom(CELLULAR_DATA_SERVICE_SWITCH);
@@ -326,6 +332,11 @@
         return mDeviceStateHelper;
     }
 
+    /** Returns the {@link VonrHelper}. */
+    public VonrHelper getVonrHelper() {
+        return mVonrHelper;
+    }
+
     /** Updates duration segments and calls {@link PersistAtomsStorage#flushAtoms()}. */
     public void flushAtomsStorage() {
         concludeAll();
@@ -1012,7 +1023,9 @@
                 session.handoverInProgress,
                 session.isIwlanCrossSimAtStart,
                 session.isIwlanCrossSimAtEnd,
-                session.isIwlanCrossSimAtConnected);
+                session.isIwlanCrossSimAtConnected,
+                session.vonrEnabled);
+
     }
 
     private static StatsEvent buildStatsEvent(IncomingSms sms) {
diff --git a/src/java/com/android/internal/telephony/metrics/PerSimStatus.java b/src/java/com/android/internal/telephony/metrics/PerSimStatus.java
index a8c7421..70ff491 100644
--- a/src/java/com/android/internal/telephony/metrics/PerSimStatus.java
+++ b/src/java/com/android/internal/telephony/metrics/PerSimStatus.java
@@ -303,7 +303,7 @@
     }
 
     /** Returns true if VoNR is enabled */
-    private static boolean isVonrEnabled(Phone phone) {
+    static boolean isVonrEnabled(Phone phone) {
         TelephonyManager telephonyManager =
                 phone.getContext()
                         .getSystemService(TelephonyManager.class);
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index 5dbf645..9cf53c9 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -36,6 +36,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.wifi.WifiInfo;
@@ -69,6 +70,7 @@
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.analytics.TelephonyAnalytics;
 import com.android.internal.telephony.analytics.TelephonyAnalytics.CallAnalytics;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
@@ -156,6 +158,7 @@
      */
     private final VoiceCallRatTracker mRatUsage = new VoiceCallRatTracker();
 
+    private final @NonNull FeatureFlags mFlags;
     private final int mPhoneId;
     private final Phone mPhone;
 
@@ -165,10 +168,13 @@
     private final DeviceStateHelper mDeviceStateHelper =
             PhoneFactory.getMetricsCollector().getDeviceStateHelper();
 
-    public VoiceCallSessionStats(int phoneId, Phone phone) {
+    private final VonrHelper mVonrHelper =
+            PhoneFactory.getMetricsCollector().getVonrHelper();
+
+    public VoiceCallSessionStats(int phoneId, Phone phone, @NonNull FeatureFlags featureFlags) {
         mPhoneId = phoneId;
         mPhone = phone;
-
+        mFlags = featureFlags;
         DataConnectionStateTracker.getInstance(phoneId).start(phone);
     }
 
@@ -457,6 +463,10 @@
         @VideoState int videoState = conn.getVideoState();
         VoiceCallSession proto = new VoiceCallSession();
 
+        if (mFlags.vonrEnabledMetric()) {
+            mVonrHelper.updateVonrEnabledState();
+        }
+
         proto.bearerAtStart = bearer;
         proto.bearerAtEnd = bearer;
         proto.direction = getDirection(conn);
@@ -556,6 +566,10 @@
         // Set device fold state
         proto.foldState = mDeviceStateHelper.getFoldState();
 
+        if (mFlags.vonrEnabledMetric()) {
+            proto.vonrEnabled = mVonrHelper.getVonrEnabled(mPhone.getSubId());
+        }
+
         mAtomsStorage.addVoiceCallSession(proto);
 
         // merge RAT usages to PersistPullers when the call session ends (i.e. no more active calls)
diff --git a/src/java/com/android/internal/telephony/metrics/VonrHelper.java b/src/java/com/android/internal/telephony/metrics/VonrHelper.java
new file mode 100644
index 0000000..8f14a86
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/VonrHelper.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static com.android.internal.telephony.metrics.MetricsCollector.getPhonesIfAny;
+import static com.android.internal.telephony.metrics.PerSimStatus.isVonrEnabled;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.flags.FeatureFlags;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Vonr state handler.
+ *
+ * <p>This class is instantiated in {@link MetricsCollector}.
+*/
+public class VonrHelper {
+    private final @NonNull FeatureFlags mFlags;
+
+    private Handler mHandler;
+    private Map<Integer, Boolean> mPhoneVonrState = new ConcurrentHashMap<>();
+
+    public VonrHelper(@NonNull FeatureFlags featureFlags) {
+        this.mFlags = featureFlags;
+        if (mFlags.vonrEnabledMetric()) {
+            HandlerThread mHandlerThread = new HandlerThread("VonrHelperThread");
+            mHandlerThread.start();
+            mHandler = new Handler(mHandlerThread.getLooper());
+        }
+    }
+
+    /** Update vonr_enabled state */
+    public void updateVonrEnabledState() {
+        if (mFlags.vonrEnabledMetric()) {
+            mHandler.post(mVonrRunnable);
+        }
+    }
+
+    @VisibleForTesting
+    protected Runnable mVonrRunnable =
+            new Runnable() {
+                @Override
+                public void run() {
+                    mPhoneVonrState.clear();
+                    for (Phone phone : getPhonesIfAny()) {
+                        mPhoneVonrState.put(phone.getSubId(), isVonrEnabled(phone));
+                    }
+                }
+            };
+
+    /** Get vonr_enabled per subId */
+    public boolean getVonrEnabled(int subId) {
+        if (mFlags.vonrEnabledMetric()) {
+            return mPhoneVonrState.getOrDefault(subId, false);
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
index 46e6a28..f1845c5 100644
--- a/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
+++ b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
@@ -34,6 +34,7 @@
 
     private static final String TAG = "CellularIdentifierDisclosureNotifier";
     private static CellularIdentifierDisclosureNotifier sInstance = null;
+    private boolean mEnabled = false;
 
     @VisibleForTesting
     public CellularIdentifierDisclosureNotifier() {}
@@ -59,4 +60,27 @@
 
         return sInstance;
     }
+
+    /**
+     * Re-enable if previously disabled. This means that {@code addDisclsoure} will start tracking
+     * disclosures again and potentially emitting notifications.
+     */
+    public void enable() {
+        Rlog.d(TAG, "enabled");
+        mEnabled = true;
+    }
+
+    /**
+     * Clear all internal state and prevent further notifications until optionally re-enabled.
+     * This can be used to in response to a user disabling the feature to emit notifications.
+     * If {@code addDisclosure} is called while in a disabled state, disclosures will be dropped.
+     */
+    public void disable() {
+        Rlog.d(TAG, "disabled");
+        mEnabled = false;
+    }
+
+    public boolean isEnabled() {
+        return mEnabled;
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index c8510eb..935da5a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -21,6 +21,7 @@
 import static com.android.internal.telephony.Phone.EVENT_ICC_CHANGED;
 import static com.android.internal.telephony.Phone.EVENT_IMS_DEREGISTRATION_TRIGGERED;
 import static com.android.internal.telephony.Phone.EVENT_RADIO_AVAILABLE;
+import static com.android.internal.telephony.Phone.EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE;
 import static com.android.internal.telephony.Phone.EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE;
 import static com.android.internal.telephony.Phone.EVENT_SRVCC_STATE_CHANGED;
 import static com.android.internal.telephony.Phone.EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED;
@@ -2858,4 +2859,82 @@
         verify(mIdentifierDisclosureNotifier, never())
                 .addDisclosure(any(CellularIdentifierDisclosure.class));
     }
+
+    @Test
+    public void testCellularIdentifierDisclosure_noModemCallOnRadioAvailable_FlagOff() {
+        when(mFeatureFlags.enableIdentifierDisclosureTransparency()).thenReturn(false);
+        GsmCdmaPhone phoneUT = makeNewPhoneUT();
+        assertFalse(phoneUT.isIdentifierDisclosureTransparencySupported());
+
+        sendRadioAvailableToPhone(phoneUT);
+
+        verify(mMockCi, never()).setCellularIdentifierTransparencyEnabled(anyBoolean(),
+                any(Message.class));
+        assertFalse(phoneUT.isIdentifierDisclosureTransparencySupported());
+    }
+
+    @Test
+    public void testCellularIdentifierDisclosure_unsupportedByModemOnRadioAvailable() {
+        when(mFeatureFlags.enableIdentifierDisclosureTransparency()).thenReturn(true);
+        GsmCdmaPhone phoneUT = makeNewPhoneUT();
+        assertFalse(phoneUT.isIdentifierDisclosureTransparencySupported());
+
+        // The following block emulates incoming messages from the modem in the case that
+        // the modem does not support the new HAL APIs. We expect the phone instance to attempt
+        // to set cipher-identifier-transparency-enabled state when the radio becomes available.
+        sendRadioAvailableToPhone(phoneUT);
+        verify(mMockCi, times(1)).setCellularIdentifierTransparencyEnabled(anyBoolean(),
+                any(Message.class));
+        sendRequestNotSupportedToPhone(phoneUT, EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE);
+
+        assertFalse(phoneUT.isIdentifierDisclosureTransparencySupported());
+    }
+
+    @Test
+    public void testCellularIdentifierDisclosure_supportedByModem() {
+        when(mFeatureFlags.enableIdentifierDisclosureTransparency()).thenReturn(true);
+        GsmCdmaPhone phoneUT = makeNewPhoneUT();
+        assertFalse(phoneUT.isIdentifierDisclosureTransparencySupported());
+
+        // The following block emulates incoming messages from the modem in the case that
+        // the modem supports the new HAL APIs. We expect the phone instance to attempt
+        // to set cipher-identifier-transparency-enabled state when the radio becomes available.
+        sendRadioAvailableToPhone(phoneUT);
+        verify(mMockCi, times(1)).setCellularIdentifierTransparencyEnabled(anyBoolean(),
+                any(Message.class));
+        sendIdentifierDisclosureEnabledSuccessToPhone(phoneUT);
+
+        assertTrue(phoneUT.isIdentifierDisclosureTransparencySupported());
+    }
+
+    private void sendRadioAvailableToPhone(GsmCdmaPhone phone) {
+        phone.sendMessage(phone.obtainMessage(EVENT_RADIO_AVAILABLE,
+                new AsyncResult(null, new int[]{ServiceState.RIL_RADIO_TECHNOLOGY_GSM}, null)));
+        processAllMessages();
+    }
+
+    private void sendRequestNotSupportedToPhone(GsmCdmaPhone phone, int eventId) {
+        phone.sendMessage(phone.obtainMessage(eventId, new AsyncResult(null, null,
+                new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED))));
+        processAllMessages();
+    }
+
+    private void sendIdentifierDisclosureEnabledSuccessToPhone(GsmCdmaPhone phone) {
+        phone.sendMessage(phone.obtainMessage(EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE,
+                new AsyncResult(null, null, null)));
+        processAllMessages();
+    }
+
+    private GsmCdmaPhone makeNewPhoneUT() {
+        return new GsmCdmaPhone(
+                mContext,
+                mMockCi,
+                mNotifier,
+                true,
+                0,
+                PhoneConstants.PHONE_TYPE_GSM,
+                mTelephonyComponentFactory,
+                (c, p) -> mImsManager,
+                mFeatureFlags);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index b8ae894..0e1135e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -95,6 +95,7 @@
     private UiccCard mActiveCard;
     private UiccPort mActivePort;
     private ServiceStateStats mServiceStateStats;
+    private VonrHelper mVonrHelper;
 
     private MetricsCollector mMetricsCollector;
 
@@ -107,8 +108,10 @@
         mActiveCard = mock(UiccCard.class);
         mActivePort = mock(UiccPort.class);
         mServiceStateStats = mock(ServiceStateStats.class);
+        mVonrHelper = mock(VonrHelper.class);
         mMetricsCollector =
-                new MetricsCollector(mContext, mPersistAtomsStorage, mDeviceStateHelper);
+                new MetricsCollector(mContext, mPersistAtomsStorage,
+                        mDeviceStateHelper, mVonrHelper);
         doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
         doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
index 00ba0f8..1498eb4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -44,6 +44,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.annotation.NonNull;
 import android.os.Looper;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation.NetworkType;
@@ -68,6 +69,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
@@ -114,12 +116,14 @@
     private GsmCdmaCall mCsCall1;
     private ImsPhoneCall mImsCall0;
     private ImsPhoneCall mImsCall1;
+    private VonrHelper mVonrHelper;
 
     private static class TestableVoiceCallSessionStats extends VoiceCallSessionStats {
         private long mTimeMillis = 0L;
 
-        TestableVoiceCallSessionStats(int phoneId, Phone phone) {
-            super(phoneId, phone);
+        TestableVoiceCallSessionStats(int phoneId, Phone phone,
+                @NonNull FeatureFlags featureFlags) {
+            super(phoneId, phone, featureFlags);
         }
 
         @Override
@@ -160,6 +164,7 @@
         mCsCall1 = mock(GsmCdmaCall.class);
         mImsCall0 = mock(ImsPhoneCall.class);
         mImsCall1 = mock(ImsPhoneCall.class);
+        mVonrHelper = mock(VonrHelper.class);
         replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mSecondPhone});
         doReturn(CARRIER_ID_SLOT_0).when(mPhone).getCarrierId();
         // mPhone's mContext/mSST/mServiceState has been set up by TelephonyTest
@@ -212,10 +217,14 @@
             Looper.prepare();
         }
 
-        mVoiceCallSessionStats0 = new TestableVoiceCallSessionStats(0, mPhone);
+        doReturn(mVonrHelper).when(mMetricsCollector).getVonrHelper();
+
+        mVoiceCallSessionStats0 = new TestableVoiceCallSessionStats(0, mPhone, mFeatureFlags);
         mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
-        mVoiceCallSessionStats1 = new TestableVoiceCallSessionStats(1, mSecondPhone);
+        mVoiceCallSessionStats1 = new TestableVoiceCallSessionStats(1, mSecondPhone, mFeatureFlags);
         mVoiceCallSessionStats1.onServiceStateChanged(mSecondServiceState);
+
+        doReturn(true).when(mFeatureFlags).vonrEnabledMetric();
     }
 
     @After
@@ -2686,6 +2695,63 @@
         assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
     }
 
+    @Test
+    @SmallTest
+    public void singleCall_vonrEnabled() {
+        setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(false).when(mImsConnection0).isIncoming();
+        doReturn(2000L).when(mImsConnection0).getCreateTime();
+        doReturn(0L).when(mImsConnection0).getDurationMillis();
+        doReturn(mImsCall0).when(mImsConnection0).getCall();
+        doReturn(true).when(mVonrHelper).getVonrEnabled(anyInt());
+        doReturn(new ArrayList(List.of(mImsConnection0))).when(mImsCall0).getConnections();
+        VoiceCallSession expectedCall =
+                makeSlot0CallProto(
+                        VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS,
+                        VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
+                        TelephonyManager.NETWORK_TYPE_LTE,
+                        ImsReasonInfo.CODE_REMOTE_CALL_DECLINE);
+        expectedCall.setupDurationMillis = 200;
+        expectedCall.setupFailed = true;
+        expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
+        expectedCall.mainCodecQuality =
+                VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
+        expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        expectedCall.callDuration = VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
+        expectedCall.vonrEnabled = true;
+        VoiceCallRatUsage expectedRatUsage =
+                makeRatUsageProto(
+                        CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
+        final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+        mVoiceCallSessionStats0.setTimeMillis(2000L);
+        doReturn(Call.State.DIALING).when(mImsCall0).getState();
+        doReturn(Call.State.DIALING).when(mImsConnection0).getState();
+        mVoiceCallSessionStats0.onImsDial(mImsConnection0);
+        mVoiceCallSessionStats0.setTimeMillis(2100L);
+        mVoiceCallSessionStats0.onAudioCodecChanged(
+                mImsConnection0, ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB);
+        mVoiceCallSessionStats0.setTimeMillis(2200L);
+        doReturn(Call.State.ALERTING).when(mImsCall0).getState();
+        doReturn(Call.State.ALERTING).when(mImsConnection0).getState();
+        mVoiceCallSessionStats0.onCallStateChanged(mImsCall0);
+        mVoiceCallSessionStats0.setTimeMillis(12000L);
+        mVoiceCallSessionStats0.onImsCallTerminated(
+                mImsConnection0, new ImsReasonInfo(ImsReasonInfo.CODE_REMOTE_CALL_DECLINE, 0));
+
+        ArgumentCaptor<VoiceCallSession> callCaptor =
+                ArgumentCaptor.forClass(VoiceCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+        verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+        assertProtoEquals(expectedCall, callCaptor.getValue());
+        assertThat(ratUsage.get()).hasLength(1);
+        assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
+    }
+
     private AtomicReference<VoiceCallRatUsage[]> setupRatUsageCapture() {
         final AtomicReference<VoiceCallRatUsage[]> ratUsage = new AtomicReference<>(null);
         doAnswer(
@@ -2773,6 +2839,7 @@
         call.isRoaming = false;
         call.setupBeginMillis = 0L;
         call.signalStrengthAtEnd = 2;
+        call.vonrEnabled = false;
         return call;
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VonrHelperTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VonrHelperTest.java
new file mode 100644
index 0000000..04cd925
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VonrHelperTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.annotation.NonNull;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class VonrHelperTest extends TelephonyTest {
+    private static final int SUBID = 1;
+
+    private static class TestableVonrHelper extends VonrHelper {
+        TestableVonrHelper(@NonNull FeatureFlags featureFlags) {
+            super(featureFlags);
+        }
+
+        @Override
+        public void updateVonrEnabledState() {
+            mVonrRunnable.run();
+        }
+    }
+
+    private TestableVonrHelper mVonrHelper;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        doReturn(SUBID).when(mPhone).getSubId();
+        doReturn(false).when(mTelephonyManager).isVoNrEnabled();
+        mVonrHelper = new TestableVonrHelper(mFeatureFlags);
+        doReturn(true).when(mFeatureFlags).vonrEnabledMetric();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void vonr_enabled() {
+        doReturn(true).when(mTelephonyManager).isVoNrEnabled();
+
+        mVonrHelper.updateVonrEnabledState();
+
+        assertThat(mVonrHelper.getVonrEnabled(SUBID)).isTrue();
+    }
+
+    @Test
+    @SmallTest
+    public void vonr_disabled() {
+        mVonrHelper.updateVonrEnabledState();
+
+        assertThat(mVonrHelper.getVonrEnabled(SUBID)).isFalse();
+    }
+}