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.internal.telephony; 18 19 import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID; 20 21 import android.content.Context; 22 import android.hardware.radio.V1_0.RadioError; 23 import android.provider.Settings; 24 import android.telephony.AnomalyReporter; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.telephony.Rlog; 28 29 import java.util.HashMap; 30 import java.util.UUID; 31 32 /** 33 * This class aims to detect radio bug based on wakelock timeout and system error. 34 * 35 * {@hide} 36 */ 37 public class RadioBugDetector { 38 private static final String TAG = "RadioBugDetector"; 39 40 /** Radio error constants */ 41 private static final int RADIO_BUG_NONE = 0x00; 42 private static final int RADIO_BUG_REPETITIVE_WAKELOCK_TIMEOUT_ERROR = 0x01; 43 @VisibleForTesting 44 protected static final int RADIO_BUG_REPETITIVE_SYSTEM_ERROR = 0x02; 45 46 /** 47 * Default configuration values for radio bug detection. 48 * The value should be large enough to avoid false alarm. From past log analysis, 10 wakelock 49 * timeout took around 1 hour. 100 accumulated system_err is an estimation for abnormal radio. 50 */ 51 private static final int DEFAULT_WAKELOCK_TIMEOUT_COUNT_THRESHOLD = 10; 52 private static final int DEFAULT_SYSTEM_ERROR_COUNT_THRESHOLD = 100; 53 54 private Context mContext; 55 private int mContinuousWakelockTimoutCount = 0; 56 private int mRadioBugStatus = RADIO_BUG_NONE; 57 private int mSlotId; 58 private int mWakelockTimeoutThreshold = 0; 59 private int mSystemErrorThreshold = 0; 60 61 private HashMap<Integer, Integer> mSysErrRecord = new HashMap<Integer, Integer>(); 62 63 /** Constructor */ RadioBugDetector(Context context, int slotId)64 public RadioBugDetector(Context context, int slotId) { 65 mContext = context; 66 mSlotId = slotId; 67 init(); 68 } 69 init()70 private void init() { 71 mWakelockTimeoutThreshold = Settings.Global.getInt( 72 mContext.getContentResolver(), 73 Settings.Global.RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD, 74 DEFAULT_WAKELOCK_TIMEOUT_COUNT_THRESHOLD); 75 mSystemErrorThreshold = Settings.Global.getInt( 76 mContext.getContentResolver(), 77 Settings.Global.RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD, 78 DEFAULT_SYSTEM_ERROR_COUNT_THRESHOLD); 79 } 80 81 /** 82 * Detect radio bug and notify this issue once the threshold is reached. 83 * 84 * @param requestType The command type information we retrieved 85 * @param error The error we received 86 */ detectRadioBug(int requestType, int error)87 public synchronized void detectRadioBug(int requestType, int error) { 88 /** 89 * When this function is executed, it means RIL is alive. So, reset WakelockTimeoutCount. 90 * Regarding SYSTEM_ERR, although RIL is alive, the connection with modem may be broken. 91 * For this error, accumulate the count and check if broadcast should be sent or not. 92 * For normal response or other error, reset the count of SYSTEM_ERR of the specific 93 * request type and mRadioBugStatus if necessary 94 */ 95 mContinuousWakelockTimoutCount = 0; 96 if (error == RadioError.SYSTEM_ERR) { 97 int errorCount = mSysErrRecord.getOrDefault(requestType, 0); 98 errorCount++; 99 mSysErrRecord.put(requestType, errorCount); 100 broadcastBug(true); 101 } else { 102 // Reset system error count if we get non-system error response. 103 mSysErrRecord.remove(requestType); 104 if (!isFrequentSystemError()) { 105 mRadioBugStatus = RADIO_BUG_NONE; 106 } 107 } 108 } 109 110 /** 111 * When wakelock timeout is detected, accumulate its count and check if broadcast should be 112 * sent or not. 113 */ processWakelockTimeout()114 public void processWakelockTimeout() { 115 mContinuousWakelockTimoutCount++; 116 broadcastBug(false); 117 } 118 broadcastBug(boolean isSystemError)119 private synchronized void broadcastBug(boolean isSystemError) { 120 if (isSystemError) { 121 if (!isFrequentSystemError()) { 122 return; 123 } 124 } else { 125 if (mContinuousWakelockTimoutCount < mWakelockTimeoutThreshold) { 126 return; 127 } 128 } 129 130 // Notify that the RIL is stuck if SYSTEM_ERR or WAKE_LOCK_TIMEOUT returned by vendor 131 // RIL is more than the threshold times. 132 if (mRadioBugStatus == RADIO_BUG_NONE) { 133 mRadioBugStatus = isSystemError ? RADIO_BUG_REPETITIVE_SYSTEM_ERROR : 134 RADIO_BUG_REPETITIVE_WAKELOCK_TIMEOUT_ERROR; 135 String message = "Repeated radio error " + mRadioBugStatus + " on slot " + mSlotId; 136 Rlog.d(TAG, message); 137 138 Phone phone = PhoneFactory.getPhone(mSlotId); 139 int carrierId = phone == null ? UNKNOWN_CARRIER_ID : phone.getCarrierId(); 140 // Using fixed UUID to avoid duplicate bugreport notification 141 AnomalyReporter.reportAnomaly( 142 UUID.fromString("d264ead0-3f05-11ea-b77f-2e728ce88125"), 143 message, carrierId); 144 } 145 } 146 isFrequentSystemError()147 private boolean isFrequentSystemError() { 148 int countForError = 0; 149 boolean error = false; 150 for (int count : mSysErrRecord.values()) { 151 countForError += count; 152 if (countForError >= mSystemErrorThreshold) { 153 error = true; 154 break; 155 } 156 } 157 return error; 158 } 159 160 @VisibleForTesting getRadioBugStatus()161 public int getRadioBugStatus() { 162 return mRadioBugStatus; 163 } 164 165 @VisibleForTesting getWakelockTimeoutThreshold()166 public int getWakelockTimeoutThreshold() { 167 return mWakelockTimeoutThreshold; 168 } 169 170 @VisibleForTesting getSystemErrorThreshold()171 public int getSystemErrorThreshold() { 172 return mSystemErrorThreshold; 173 } 174 175 @VisibleForTesting getWakelockTimoutCount()176 public int getWakelockTimoutCount() { 177 return mContinuousWakelockTimoutCount; 178 } 179 180 } 181 182