1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.phone; 18 19 import android.annotation.IntDef; 20 import android.app.KeyguardManager; 21 import android.content.Context; 22 import android.metrics.LogMaker; 23 import android.os.Build; 24 import android.telephony.TelephonyManager; 25 import android.util.Log; 26 27 import androidx.annotation.Nullable; 28 29 import com.android.internal.logging.MetricsLogger; 30 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 35 /** 36 * EmergencyCallMetricsLogger is a utility to collect metrics of emergency calls 37 */ 38 class EmergencyDialerMetricsLogger { 39 private static final String LOG_TAG = "EmergencyDialerLogger"; 40 41 @IntDef({ 42 DialedFrom.TRADITIONAL_DIALPAD, 43 DialedFrom.SHORTCUT, 44 DialedFrom.FASTER_LAUNCHER_DIALPAD, 45 }) 46 @Retention(RetentionPolicy.SOURCE) 47 @interface DialedFrom { 48 int TRADITIONAL_DIALPAD = 0; 49 int SHORTCUT = 1; 50 int FASTER_LAUNCHER_DIALPAD = 2; 51 } 52 53 @IntDef({ 54 LaunchedFrom.UNDEFINED, 55 LaunchedFrom.LOCK_SCREEN, 56 LaunchedFrom.POWER_KEY_MENU, 57 }) 58 @Retention(RetentionPolicy.SOURCE) 59 @interface LaunchedFrom { 60 int UNDEFINED = 0; 61 int LOCK_SCREEN = 1; 62 int POWER_KEY_MENU = 2; 63 } 64 65 @IntDef({ 66 PhoneNumberType.HAS_SHORTCUT, 67 PhoneNumberType.NO_SHORTCUT, 68 PhoneNumberType.NOT_EMERGENCY_NUMBER, 69 }) 70 @Retention(RetentionPolicy.SOURCE) 71 @interface PhoneNumberType { 72 int HAS_SHORTCUT = 0; 73 int NO_SHORTCUT = 1; 74 int NOT_EMERGENCY_NUMBER = 2; 75 } 76 77 @IntDef({ 78 UiModeErrorCode.UNSPECIFIED_ERROR, 79 UiModeErrorCode.SUCCESS, 80 UiModeErrorCode.CONFIG_ENTRY_POINT, 81 UiModeErrorCode.CONFIG_SIM_OPERATOR, 82 UiModeErrorCode.UNSUPPORTED_COUNTRY, 83 UiModeErrorCode.AIRPLANE_MODE, 84 UiModeErrorCode.NO_PROMOTED_NUMBER, 85 UiModeErrorCode.NO_CAPABLE_PHONE, 86 }) 87 @Retention(RetentionPolicy.SOURCE) 88 @interface UiModeErrorCode { 89 int UNSPECIFIED_ERROR = -1; 90 int SUCCESS = 0; 91 int CONFIG_ENTRY_POINT = 1; 92 int CONFIG_SIM_OPERATOR = 2; 93 int UNSUPPORTED_COUNTRY = 3; 94 int AIRPLANE_MODE = 4; 95 int NO_PROMOTED_NUMBER = 5; 96 int NO_CAPABLE_PHONE = 6; 97 } 98 99 private class TelephonyInfo { 100 private final String mNetworkCountryIso; 101 private final String mNetworkOperator; 102 TelephonyInfo(String networkCountryIso, String networkOperator)103 TelephonyInfo(String networkCountryIso, String networkOperator) { 104 mNetworkCountryIso = networkCountryIso; 105 mNetworkOperator = networkOperator; 106 } 107 } 108 109 private final MetricsLogger mMetricsLogger = new MetricsLogger(); 110 111 private final Context mAppContext; 112 113 @LaunchedFrom 114 private int mLaunchedFrom; 115 @UiModeErrorCode 116 private int mUiModeErrorCode; 117 EmergencyDialerMetricsLogger(Context context)118 EmergencyDialerMetricsLogger(Context context) { 119 mAppContext = context.getApplicationContext(); 120 } 121 122 /** 123 * Log when Emergency Dialer is launched. 124 * - Where the user launch Emergency Dialer from. 125 * - Whether shortcut view is enabled, and the reason why it's not enabled. 126 * 127 * @param entryType 128 * @param uiModeErrorCode 129 */ logLaunchEmergencyDialer(int entryType, @UiModeErrorCode int uiModeErrorCode)130 public void logLaunchEmergencyDialer(int entryType, 131 @UiModeErrorCode int uiModeErrorCode) { 132 final @EmergencyDialerMetricsLogger.LaunchedFrom int launchedFrom; 133 if (entryType == EmergencyDialer.ENTRY_TYPE_LOCKSCREEN_BUTTON) { 134 launchedFrom = EmergencyDialerMetricsLogger.LaunchedFrom.LOCK_SCREEN; 135 } else if (entryType == EmergencyDialer.ENTRY_TYPE_POWER_MENU) { 136 launchedFrom = EmergencyDialerMetricsLogger.LaunchedFrom.POWER_KEY_MENU; 137 } else { 138 launchedFrom = EmergencyDialerMetricsLogger.LaunchedFrom.UNDEFINED; 139 } 140 141 mLaunchedFrom = launchedFrom; 142 mUiModeErrorCode = uiModeErrorCode; 143 } 144 145 /** 146 * Log when user tring to place an emergency call. 147 * - Which UI (traditional dialpad, shortcut button, dialpad in shortcut view) the user place 148 * the call from. 149 * - The number is promoted in shortcut view or not, or not even an emergency number? 150 * - Whether the device is locked. 151 * - Network country ISO and network operator. 152 * 153 * @param dialedFrom 154 * @param phoneNumberType 155 * @param phoneInfo 156 */ logPlaceCall(@ialedFrom int dialedFrom, @PhoneNumberType int phoneNumberType, @Nullable ShortcutViewUtils.PhoneInfo phoneInfo)157 public void logPlaceCall(@DialedFrom int dialedFrom, 158 @PhoneNumberType int phoneNumberType, 159 @Nullable ShortcutViewUtils.PhoneInfo phoneInfo) { 160 TelephonyInfo telephonyInfo = getTelephonyInfo(phoneNumberType, phoneInfo); 161 final KeyguardManager keyguard = mAppContext.getSystemService(KeyguardManager.class); 162 163 logBeforeMakeCall(dialedFrom, phoneNumberType, keyguard.isKeyguardLocked(), 164 telephonyInfo.mNetworkCountryIso, telephonyInfo.mNetworkOperator); 165 } 166 getTelephonyInfo(@honeNumberType int phoneNumberType, @Nullable ShortcutViewUtils.PhoneInfo phoneInfo)167 private TelephonyInfo getTelephonyInfo(@PhoneNumberType int phoneNumberType, 168 @Nullable ShortcutViewUtils.PhoneInfo phoneInfo) { 169 final TelephonyManager telephonyManager = mAppContext.getSystemService( 170 TelephonyManager.class); 171 final TelephonyManager subTelephonyManager; 172 final String networkCountryIso; 173 final String networkOperator; 174 if (phoneNumberType == PhoneNumberType.HAS_SHORTCUT && phoneInfo != null) { 175 subTelephonyManager = telephonyManager.createForSubscriptionId(phoneInfo.getSubId()); 176 networkCountryIso = phoneInfo.getCountryIso(); 177 } else { 178 // No specific phone to make this call. Take information of default network. 179 subTelephonyManager = null; 180 networkCountryIso = telephonyManager.getNetworkCountryIso(); 181 } 182 if (subTelephonyManager != null) { 183 networkOperator = subTelephonyManager.getNetworkOperator(); 184 } else { 185 // This could be: 186 // - No specific phone to make this call. 187 // - Subscription changed! Maybe the device roamed to another network? 188 // Take information of default network. 189 networkOperator = telephonyManager.getNetworkOperator(); 190 } 191 192 return new TelephonyInfo(networkCountryIso, networkOperator); 193 } 194 logBeforeMakeCall(@ialedFrom int dialedFrom, @PhoneNumberType int phoneNumberType, boolean isDeviceLocked, String networkCountryIso, String networkOperator)195 private void logBeforeMakeCall(@DialedFrom int dialedFrom, 196 @PhoneNumberType int phoneNumberType, 197 boolean isDeviceLocked, 198 String networkCountryIso, 199 String networkOperator) { 200 if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { 201 Log.d(LOG_TAG, "EmergencyDialer session: dialedFrom=" + dialFromToString(dialedFrom) 202 + ", launchedFrom=" + launchedFromToString(mLaunchedFrom) 203 + ", uimode=" + uiModeErrorCodeToString(mUiModeErrorCode) 204 + ", type=" + phoneNumberTypeToString(phoneNumberType) 205 + ", locked=" + isDeviceLocked 206 + ", country=" + networkCountryIso 207 + ", operator=" + networkOperator); 208 } 209 mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER_MAKE_CALL_V2) 210 .setType(MetricsEvent.TYPE_ACTION) 211 .setSubtype(dialedFrom) 212 .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_LAUNCH_FROM, mLaunchedFrom) 213 .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_UI_MODE_ERROR_CODE, 214 mUiModeErrorCode) 215 .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_TYPE, 216 phoneNumberType) 217 .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IS_DEVICE_LOCKED, 218 isDeviceLocked ? 1 : 0) 219 .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_NETWORK_COUNTRY_ISO, 220 networkCountryIso) 221 .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_NETWORK_OPERATOR, 222 networkOperator) 223 .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_RADIO_VERSION, 224 Build.getRadioVersion()) 225 ); 226 } 227 dialFromToString(@ialedFrom int dialedFrom)228 private String dialFromToString(@DialedFrom int dialedFrom) { 229 switch (dialedFrom) { 230 case DialedFrom.TRADITIONAL_DIALPAD: 231 return "traditional"; 232 case DialedFrom.SHORTCUT: 233 return "shortcut"; 234 case DialedFrom.FASTER_LAUNCHER_DIALPAD: 235 return "dialpad"; 236 default: 237 return "unknown_error"; 238 } 239 } 240 launchedFromToString(@aunchedFrom int launchedFrom)241 private String launchedFromToString(@LaunchedFrom int launchedFrom) { 242 switch (launchedFrom) { 243 case LaunchedFrom.UNDEFINED: 244 return "undefined"; 245 case LaunchedFrom.LOCK_SCREEN: 246 return "lockscreen"; 247 case LaunchedFrom.POWER_KEY_MENU: 248 return "powermenu"; 249 default: 250 return "unknown_error"; 251 } 252 } 253 phoneNumberTypeToString(@honeNumberType int phoneNumberType)254 private String phoneNumberTypeToString(@PhoneNumberType int phoneNumberType) { 255 switch (phoneNumberType) { 256 case PhoneNumberType.HAS_SHORTCUT: 257 return "has_shortcut"; 258 case PhoneNumberType.NO_SHORTCUT: 259 return "no_shortcut"; 260 case PhoneNumberType.NOT_EMERGENCY_NUMBER: 261 return "not_emergency"; 262 default: 263 return "unknown_error"; 264 } 265 } 266 uiModeErrorCodeToString(@iModeErrorCode int uiModeErrorCode)267 private String uiModeErrorCodeToString(@UiModeErrorCode int uiModeErrorCode) { 268 switch (uiModeErrorCode) { 269 case UiModeErrorCode.UNSPECIFIED_ERROR: 270 return "unspecified_error"; 271 case UiModeErrorCode.SUCCESS: 272 return "success"; 273 case UiModeErrorCode.CONFIG_ENTRY_POINT: 274 return "config_entry_point"; 275 case UiModeErrorCode.CONFIG_SIM_OPERATOR: 276 return "config_sim_operator"; 277 case UiModeErrorCode.UNSUPPORTED_COUNTRY: 278 return "unsupported_country"; 279 case UiModeErrorCode.AIRPLANE_MODE: 280 return "airplane_mode"; 281 case UiModeErrorCode.NO_PROMOTED_NUMBER: 282 return "no_promoted_number"; 283 case UiModeErrorCode.NO_CAPABLE_PHONE: 284 return "no_capable_phone"; 285 default: 286 return "unknown_error"; 287 } 288 } 289 } 290