Add metrics for emergency dialer
These include,
1. The entry point where user starts emergency dialer.
2. The duration that user stays at emergency dialer,
and if user takes further action before leaving this page.
3. The context when user has intention to make a phone call.
Bug: 111378820
Test: manual and check the result of
'adb logcat -b events | grep sysui_multi_action'
Change-Id: I3612008aadebbcbd0180ab6f238359984416920b
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 4b00061..b7f695d 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,12 +32,18 @@
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;
@@ -69,6 +76,8 @@
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 +114,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,
@@ -185,6 +262,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,6 +312,12 @@
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
@@ -456,6 +555,13 @@
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)) {
@@ -489,11 +595,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);
@@ -595,6 +703,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) {
@@ -640,6 +753,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;
@@ -664,11 +782,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);
}
@@ -706,6 +828,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.
@@ -722,6 +845,10 @@
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);
@@ -730,6 +857,11 @@
} 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());
@@ -1114,4 +1246,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;
+ }
}