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