Merge "PhoneInterfaceManager: Surface APIs to use WorkSource."
diff --git a/res/layout/emergency_dialer.xml b/res/layout/emergency_dialer.xml
index cdb9530..7f99664 100644
--- a/res/layout/emergency_dialer.xml
+++ b/res/layout/emergency_dialer.xml
@@ -22,6 +22,7 @@
     <!-- Emergency dialer shortcuts layout-->
     <FrameLayout
         android:id="@+id/emergency_dialer_shortcuts"
+        android:accessibilityPaneTitle="@string/emergencyDialerIconLabel"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="gone">
@@ -52,6 +53,7 @@
     <!--Emergency Dialer Layout-->
     <FrameLayout
         android:id="@+id/emergency_dialer"
+        android:accessibilityPaneTitle="@string/pane_title_emergency_dialpad"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:paddingLeft="36dp"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6b7a105..7773af6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1399,6 +1399,13 @@
     -->
     <string name="description_dialpad_button">show dialpad</string>
 
+    <!-- Pane title of the Emergency Dialpad
+
+     Used by AccessibilityService to announce the purpose of the pane of emergency dialpad.
+     [CHAR LIMIT=NONE]
+    -->
+    <string name="pane_title_emergency_dialpad">Emergency Dialpad</string>
+
     <!-- Visual voicemail on/off title [CHAR LIMIT=40] -->
     <string name="voicemail_visual_voicemail_switch_title">Visual Voicemail</string>
 
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 7e5b1ce..66701d5 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -23,6 +23,7 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
+import android.app.KeyguardManager;
 import android.app.WallpaperManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -31,13 +32,20 @@
 import android.database.DataSetObserver;
 import android.graphics.Color;
 import android.graphics.Point;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.media.AudioManager;
 import android.media.ToneGenerator;
+import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.os.SystemClock;
 import android.provider.Settings;
+import android.telecom.ParcelableCallAnalytics;
 import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
@@ -54,6 +62,7 @@
 import android.text.TextWatcher;
 import android.text.method.DialerKeyListener;
 import android.text.style.TtsSpan;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.HapticFeedbackConstants;
@@ -61,13 +70,17 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.AccessibilityDelegate;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.TextView;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.phone.common.dialpad.DialpadKeyButton;
 import com.android.phone.common.util.ViewUtil;
 import com.android.phone.common.widget.ResizingTextEditText;
@@ -105,13 +118,81 @@
 public class EmergencyDialer extends Activity implements View.OnClickListener,
         View.OnLongClickListener, View.OnKeyListener, TextWatcher,
         DialpadKeyButton.OnPressedListener, ColorExtractor.OnColorsChangedListener,
-        EmergencyShortcutButton.OnConfirmClickListener {
+        EmergencyShortcutButton.OnConfirmClickListener, SensorEventListener {
+
+    private class MetricsWriter {
+        // Metrics constants indicating the entry type that user opened emergency dialer.
+        // This info is sent from system UI with EXTRA_ENTRY_TYPE. Please make them being
+        // in sync with those in com.android.systemui.util.EmergencyDialerConstants.
+        public static final int ENTRY_TYPE_UNKNOWN = 0;
+        public static final int ENTRY_TYPE_LOCKSCREEN_BUTTON = 1;
+        public static final int ENTRY_TYPE_POWER_MENU = 2;
+
+        // Metrics constants indicating the UI that user made phone call.
+        public static final int CALL_SOURCE_DIALPAD = 0;
+        public static final int CALL_SOURCE_SHORTCUT = 1;
+
+        // Metrics constants indicating the phone number type of a call user made.
+        public static final int PHONE_NUMBER_TYPE_GENERAL = 0;
+        public static final int PHONE_NUMBER_TYPE_EMERGENCY = 1;
+
+        // Metrics constants indicating the actions performed by user.
+        public static final int USER_ACTION_NONE = 0x0;
+        public static final int USER_ACTION_OPEN_DIALPAD = 0x1;
+        public static final int USER_ACTION_OPEN_EMERGENCY_INFO = 0x2;
+        public static final int USER_ACTION_MAKE_CALL_VIA_DIALPAD = 0x4;
+        public static final int USER_ACTION_MAKE_CALL_VIA_SHORTCUT = 0x8;
+
+        private MetricsLogger mMetricsLogger = new MetricsLogger();
+
+        public void writeMetricsForEnter() {
+            int entryType = getIntent().getIntExtra(EXTRA_ENTRY_TYPE, ENTRY_TYPE_UNKNOWN);
+            KeyguardManager keyguard = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+            mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER)
+                    .setType(MetricsEvent.TYPE_OPEN)
+                    .setSubtype(entryType)
+                    .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IS_SCREEN_LOCKED,
+                            keyguard.isKeyguardLocked() ? 1 : 0));
+        }
+
+        public void writeMetricsForExit() {
+            int entryType = getIntent().getIntExtra(EXTRA_ENTRY_TYPE, ENTRY_TYPE_UNKNOWN);
+            long userStayDuration = SystemClock.elapsedRealtime() - mUserEnterTimeMillis;
+            mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER)
+                    .setType(MetricsEvent.TYPE_CLOSE)
+                    .setSubtype(entryType)
+                    .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_USER_ACTIONS, mUserActions)
+                    .addTaggedData(
+                            MetricsEvent.FIELD_EMERGENCY_DIALER_DURATION_MS, userStayDuration));
+        }
+
+        public void writeMetricsForMakingCall(int callSource, int phoneNumberType,
+                boolean hasShortcut) {
+            mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER_MAKE_CALL)
+                    .setType(MetricsEvent.TYPE_ACTION)
+                    .setSubtype(callSource)
+                    .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_TYPE,
+                            phoneNumberType)
+                    .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_HAS_SHORTCUT,
+                            hasShortcut ? 1 : 0)
+                    .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IN_POCKET,
+                            mIsProximityNear ? 1 : 0));
+        }
+    }
+
     // Keys used with onSaveInstanceState().
     private static final String LAST_NUMBER = "lastNumber";
 
     // Intent action for this activity.
     public static final String ACTION_DIAL = "com.android.phone.EmergencyDialer.DIAL";
 
+    /**
+     * Extra included in {@link #ACTION_DIAL} to indicate the entry type that user starts
+     * the emergency dialer.
+     */
+    public static final String EXTRA_ENTRY_TYPE =
+            "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE";
+
     // List of dialer button IDs.
     private static final int[] DIALER_KEYS = new int[] {
             R.id.one, R.id.two, R.id.three,
@@ -173,6 +254,30 @@
         }
     };
 
+    /**
+     * Customize accessibility methods in View.
+     */
+    private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
+
+        /**
+         * Stop AccessiblityService from reading the title of a hidden View.
+         *
+         * <p>The crossfade animation will set the visibility of fade out view to {@link View.GONE}
+         * in the animation end. The view with an accessibility pane title would call the
+         * {@link AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED} event, which would trigger the
+         * accessibility service to read the pane title of fade out view instead of pane title of
+         * fade in view. So it need to filter out the event called by vanished pane.
+         */
+        @Override
+        public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+                    && host.getVisibility() == View.GONE) {
+                return;
+            }
+            super.onPopulateAccessibilityEvent(host, event);
+        }
+    };
+
     private String mLastNumber; // last number we tried to dial. Used to restore error dialog.
 
     // Background gradient
@@ -185,6 +290,22 @@
 
     private boolean mAreEmergencyDialerShortcutsEnabled;
 
+    private MetricsWriter mMetricsWriter;
+    private SensorManager mSensorManager;
+    private Sensor mProximitySensor;
+    private boolean mIsProximityNear = false;
+
+    /**
+     * The time, in millis, since boot when user opened emergency dialer.
+     * This is used when calculating the user stay duration for metrics data.
+     */
+    private long mUserEnterTimeMillis = 0;
+
+    /**
+     * Bit flag indicating the actions performed by user. This is used for metrics data.
+     */
+    private int mUserActions = MetricsWriter.USER_ACTION_NONE;
+
     @Override
     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
         // Do nothing
@@ -219,13 +340,19 @@
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        mMetricsWriter = new MetricsWriter();
+        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+        if (mSensorManager != null) {
+            mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+        }
+
         // Allow this activity to be displayed in front of the keyguard / lockscreen.
         setShowWhenLocked(true);
         // Allow turning screen on
         setTurnScreenOn(true);
 
-        mAreEmergencyDialerShortcutsEnabled = Settings.Global.getInt(getContentResolver(),
-                Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0;
+        mAreEmergencyDialerShortcutsEnabled = FeatureFlagUtils
+                .isEnabled(this, FeatureFlagUtils.EMERGENCY_DIAL_SHORTCUTS);
         Log.d(LOG_TAG, "Enable emergency dialer shortcut: "
                 + mAreEmergencyDialerShortcutsEnabled);
 
@@ -457,12 +584,22 @@
     public void onConfirmClick(EmergencyShortcutButton button) {
         if (button == null) return;
 
+        mUserActions |= MetricsWriter.USER_ACTION_MAKE_CALL_VIA_SHORTCUT;
+
+        // We interest on the context when user has intention to make phone call,
+        // so write metrics here for shortcut number even the call may not be created.
+        mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_SHORTCUT,
+                MetricsWriter.PHONE_NUMBER_TYPE_EMERGENCY, true);
+
         String phoneNumber = button.getPhoneNumber();
 
         if (!TextUtils.isEmpty(phoneNumber)) {
             if (DBG) Log.d(LOG_TAG, "dial emergency number: " + Rlog.pii(LOG_TAG, phoneNumber));
+            Bundle extras = new Bundle();
+            extras.putInt(TelecomManager.EXTRA_CALL_SOURCE,
+                    ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT);
             TelecomManager tm = (TelecomManager) getSystemService(TELECOM_SERVICE);
-            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null), null);
+            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null), extras);
         } else {
             Log.d(LOG_TAG, "emergency number is empty");
         }
@@ -487,11 +624,13 @@
                 return;
             }
             case R.id.floating_action_button_dialpad: {
+                mUserActions |= MetricsWriter.USER_ACTION_OPEN_DIALPAD;
                 mDigits.getText().clear();
                 switchView(mDialpadView, mEmergencyShortcutView, true);
                 return;
             }
             case R.id.emergency_info_button: {
+                mUserActions |= MetricsWriter.USER_ACTION_OPEN_EMERGENCY_INFO;
                 Intent intent = (Intent) view.getTag(R.id.tag_intent);
                 if (intent != null) {
                     startActivity(intent);
@@ -593,6 +732,11 @@
     @Override
     protected void onStart() {
         super.onStart();
+
+        mUserEnterTimeMillis = SystemClock.elapsedRealtime();
+        mUserActions = MetricsWriter.USER_ACTION_NONE;
+        mMetricsWriter.writeMetricsForEnter();
+
         // It does not support dark text theme, when emergency dialer shortcuts are enabled.
         // And set background color to black.
         if (mAreEmergencyDialerShortcutsEnabled) {
@@ -638,6 +782,11 @@
     protected void onResume() {
         super.onResume();
 
+        if (mProximitySensor != null) {
+            mSensorManager.registerListener(
+                    this, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+        }
+
         // retrieve the DTMF tone play back setting.
         mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
                 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
@@ -662,11 +811,15 @@
     @Override
     public void onPause() {
         super.onPause();
+        if (mProximitySensor != null) {
+            mSensorManager.unregisterListener(this, mProximitySensor);
+        }
     }
 
     @Override
     protected void onStop() {
         super.onStop();
+        mMetricsWriter.writeMetricsForExit();
         mColorExtractor.removeOnColorsChangedListener(this);
     }
 
@@ -704,6 +857,7 @@
      * place the call, but check to make sure it is a viable number.
      */
     private void placeCall() {
+        mUserActions |= MetricsWriter.USER_ACTION_MAKE_CALL_VIA_DIALPAD;
         mLastNumber = mDigits.getText().toString();
 
         // Convert into emergency number according to emergency conversion map.
@@ -720,11 +874,23 @@
                 playTone(ToneGenerator.TONE_PROP_NACK);
                 return;
             }
+
+            mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_DIALPAD,
+                    MetricsWriter.PHONE_NUMBER_TYPE_EMERGENCY, isShortcutNumber(mLastNumber));
+
+            Bundle extras = new Bundle();
+            extras.putInt(TelecomManager.EXTRA_CALL_SOURCE,
+                    ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD);
             TelecomManager tm = (TelecomManager) getSystemService(TELECOM_SERVICE);
-            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, mLastNumber, null), null);
+            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, mLastNumber, null), extras);
         } else {
             if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
 
+            // We interest on the context when user has intention to make phone call,
+            // so write metrics here for non-emergency numbers even these numbers are rejected.
+            mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_DIALPAD,
+                    MetricsWriter.PHONE_NUMBER_TYPE_GENERAL, false);
+
             showDialog(BAD_EMERGENCY_NUMBER_DIALOG);
         }
         mDigits.getText().delete(0, mDigits.getText().length());
@@ -927,6 +1093,9 @@
         mEmergencyShortcutView = findViewById(R.id.emergency_dialer_shortcuts);
         mDialpadView = findViewById(R.id.emergency_dialer);
 
+        mEmergencyShortcutView.setAccessibilityDelegate(mAccessibilityDelegate);
+        mDialpadView.setAccessibilityDelegate(mAccessibilityDelegate);
+
         final View dialpadButton = findViewById(R.id.floating_action_button_dialpad);
         dialpadButton.setOnClickListener(this);
 
@@ -1109,4 +1278,30 @@
                     }
                 });
     }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        float distance = event.values[0];
+        mIsProximityNear = (distance < mProximitySensor.getMaximumRange());
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        // Not used.
+    }
+
+    private boolean isShortcutNumber(String number) {
+        if (TextUtils.isEmpty(number) || mEmergencyShortcutButtonList == null) {
+            return false;
+        }
+
+        boolean isShortcut = false;
+        for (EmergencyShortcutButton button : mEmergencyShortcutButtonList) {
+            if (button != null && number.equals(button.getPhoneNumber())) {
+                isShortcut = true;
+                break;
+            }
+        }
+        return isShortcut;
+    }
 }
diff --git a/src/com/android/phone/EmergencyShortcutButton.java b/src/com/android/phone/EmergencyShortcutButton.java
index 275dac0..59b3794 100644
--- a/src/com/android/phone/EmergencyShortcutButton.java
+++ b/src/com/android/phone/EmergencyShortcutButton.java
@@ -19,6 +19,8 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
+import android.metrics.LogMaker;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -30,6 +32,9 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
 /**
  * Emergency shortcut button displays a local emergency phone number information(including phone
  * number, and phone type). To decrease false clicking, it need to click twice to confirm to place
@@ -61,6 +66,12 @@
 
     private boolean mConfirmViewHiding;
 
+    /**
+     * The time, in millis, since boot when user taps on shortcut button to reveal confirm view.
+     * This is used for metrics when calculating the interval between reveal tap and confirm tap.
+     */
+    private long mTimeOfRevealTapInMillis = 0;
+
     public EmergencyShortcutButton(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -185,6 +196,8 @@
         switch (view.getId()) {
             case R.id.emergency_call_number_info_view:
                 if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
+                    // TalkBack itself includes a prompt to confirm click action implicitly,
+                    // so we don't need an additional confirmation with second tap on button.
                     if (mOnConfirmClickListener != null) {
                         mOnConfirmClickListener.onConfirmClick(this);
                     }
@@ -193,6 +206,15 @@
                 }
                 break;
             case R.id.emergency_call_confirm_view:
+                if (mTimeOfRevealTapInMillis != 0) {
+                    long timeBetweenTwoTaps =
+                            SystemClock.elapsedRealtime() - mTimeOfRevealTapInMillis;
+                    // Reset reveal time to zero for next reveal-confirm taps pair.
+                    mTimeOfRevealTapInMillis = 0;
+
+                    writeMetricsForConfirmTap(timeBetweenTwoTaps);
+                }
+
                 if (mOnConfirmClickListener != null) {
                     mOnConfirmClickListener.onConfirmClick(this);
                 }
@@ -204,6 +226,7 @@
         mConfirmViewHiding = false;
 
         mConfirmView.setVisibility(View.VISIBLE);
+        mTimeOfRevealTapInMillis = SystemClock.elapsedRealtime();
         int centerX = mCallNumberInfoView.getLeft() + mCallNumberInfoView.getWidth() / 2;
         int centerY = mCallNumberInfoView.getTop() + mCallNumberInfoView.getHeight() / 2;
         Animator reveal = ViewAnimationUtils.createCircularReveal(
@@ -240,6 +263,8 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mConfirmView.setVisibility(INVISIBLE);
+                // Reset reveal time to zero for next reveal-confirm taps pair.
+                mTimeOfRevealTapInMillis = 0;
             }
         });
         reveal.start();
@@ -254,4 +279,12 @@
             hideSelectedButton();
         }
     };
+
+    private void writeMetricsForConfirmTap(long timeBetweenTwoTaps) {
+        LogMaker logContent = new LogMaker(MetricsEvent.EMERGENCY_DIALER_SHORTCUT_CONFIRM_TAP)
+                .setType(MetricsEvent.TYPE_ACTION)
+                .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_SHORTCUT_TAPS_INTERVAL,
+                        timeBetweenTwoTaps);
+        MetricsLogger.action(logContent);
+    }
 }