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