This CL fixes the ANR by moving possible blocking IDumpstateDevice
IPC call from main thread to background thread.
Settings develop options depends on IDumpstateDevice IPC call to
set and get Vendor Verbose Logging feature. The IPC may occasionally
get blocked in system_server which is extremely busy (e.g. during
bugreport generating). This may cause Settings app crashing with ANR.
Bug: 287126040
Bug: 280015761
Test: atest EnableVerboseVendorLoggingPreferenceControllerTest
Test: Verbose Vendor Logging regression
Test: Stress test (heavily trigger on/off during BR generating)
Merged-In: I0b88ef089097930b62dcb1cb7d6fe9990356ab5d
Change-Id: I0b88ef089097930b62dcb1cb7d6fe9990356ab5d
(cherry picked from commit f7b8d687bf
)
225 lines
8.7 KiB
Java
225 lines
8.7 KiB
Java
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.settings.development;
|
|
|
|
import android.annotation.Nullable;
|
|
import android.content.Context;
|
|
import android.hardware.dumpstate.V1_0.IDumpstateDevice;
|
|
import android.os.RemoteException;
|
|
import android.os.ServiceManager;
|
|
import android.util.Log;
|
|
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.preference.Preference;
|
|
import androidx.preference.SwitchPreference;
|
|
|
|
import com.android.settings.core.PreferenceControllerMixin;
|
|
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
|
|
import com.android.settingslib.utils.ThreadUtils;
|
|
|
|
import java.util.NoSuchElementException;
|
|
|
|
public class EnableVerboseVendorLoggingPreferenceController
|
|
extends DeveloperOptionsPreferenceController
|
|
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
|
|
|
|
private static final String TAG = "EnableVerboseVendorLoggingPreferenceController";
|
|
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
|
|
|
|
private static final String ENABLE_VERBOSE_VENDOR_LOGGING_KEY = "enable_verbose_vendor_logging";
|
|
private static final int DUMPSTATE_HAL_VERSION_UNKNOWN = -1;
|
|
private static final int DUMPSTATE_HAL_VERSION_1_0 = 0; // HIDL v1.0
|
|
private static final int DUMPSTATE_HAL_VERSION_1_1 = 1; // HIDL v1.1
|
|
private static final int DUMPSTATE_HAL_VERSION_2_0 = 2; // AIDL v1
|
|
|
|
private static final String DUMP_STATE_AIDL_SERVICE_NAME =
|
|
android.hardware.dumpstate.IDumpstateDevice.DESCRIPTOR + "/default";
|
|
|
|
private int mDumpstateHalVersion;
|
|
|
|
public EnableVerboseVendorLoggingPreferenceController(Context context) {
|
|
super(context);
|
|
mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_UNKNOWN;
|
|
}
|
|
|
|
@Override
|
|
public String getPreferenceKey() {
|
|
return ENABLE_VERBOSE_VENDOR_LOGGING_KEY;
|
|
}
|
|
|
|
@Override
|
|
public boolean isAvailable() {
|
|
// Only show preference when IDumpstateDevice AIDL or HIDL v1.1 service is available
|
|
return isIDumpstateDeviceAidlServiceAvailable() || isIDumpstateDeviceV1_1ServiceAvailable();
|
|
}
|
|
|
|
@SuppressWarnings("FutureReturnValueIgnored")
|
|
@Override
|
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
final boolean isEnabled = (Boolean) newValue;
|
|
// IDumpstateDevice IPC may be blocking when system is extremely heavily-loaded.
|
|
// Post to background thread to avoid ANR. Ignore the returned Future.
|
|
ThreadUtils.postOnBackgroundThread(() ->
|
|
setVerboseLoggingEnabled(isEnabled));
|
|
return true;
|
|
}
|
|
|
|
@SuppressWarnings("FutureReturnValueIgnored")
|
|
@Override
|
|
public void updateState(Preference preference) {
|
|
ThreadUtils.postOnBackgroundThread(() -> {
|
|
final boolean enabled = getVerboseLoggingEnabled();
|
|
ThreadUtils.getUiThreadHandler().post(() ->
|
|
((SwitchPreference) mPreference).setChecked(enabled));
|
|
}
|
|
);
|
|
}
|
|
|
|
@SuppressWarnings("FutureReturnValueIgnored")
|
|
@Override
|
|
protected void onDeveloperOptionsSwitchDisabled() {
|
|
super.onDeveloperOptionsSwitchDisabled();
|
|
ThreadUtils.postOnBackgroundThread(() ->
|
|
setVerboseLoggingEnabled(false));
|
|
((SwitchPreference) mPreference).setChecked(false);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
boolean isIDumpstateDeviceV1_1ServiceAvailable() {
|
|
IDumpstateDevice service = getDumpstateDeviceService();
|
|
if (service == null) {
|
|
if (DBG) Log.d(TAG, "IDumpstateDevice v1.1 service is not available.");
|
|
}
|
|
return service != null && mDumpstateHalVersion == DUMPSTATE_HAL_VERSION_1_1;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
boolean isIDumpstateDeviceAidlServiceAvailable() {
|
|
android.hardware.dumpstate.IDumpstateDevice aidlService = getDumpstateDeviceAidlService();
|
|
return aidlService != null;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void setVerboseLoggingEnabled(boolean enable) {
|
|
// First check if AIDL service is available
|
|
android.hardware.dumpstate.IDumpstateDevice aidlService = getDumpstateDeviceAidlService();
|
|
if (aidlService != null) {
|
|
try {
|
|
aidlService.setVerboseLoggingEnabled(enable);
|
|
} catch (RemoteException re) {
|
|
if (DBG) Log.e(TAG, "aidlService.setVerboseLoggingEnabled fail: " + re);
|
|
}
|
|
}
|
|
|
|
// Then check HIDL v1.1 service
|
|
IDumpstateDevice service = getDumpstateDeviceService();
|
|
if (service == null || mDumpstateHalVersion < DUMPSTATE_HAL_VERSION_1_1) {
|
|
if (DBG) Log.d(TAG, "setVerboseLoggingEnabled not supported.");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
android.hardware.dumpstate.V1_1.IDumpstateDevice service11 =
|
|
(android.hardware.dumpstate.V1_1.IDumpstateDevice) service;
|
|
if (service11 != null) {
|
|
service11.setVerboseLoggingEnabled(enable);
|
|
}
|
|
} catch (RemoteException | RuntimeException e) {
|
|
if (DBG) Log.e(TAG, "HIDL v1.1 setVerboseLoggingEnabled fail: " + e);
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
boolean getVerboseLoggingEnabled() {
|
|
// First check if AIDL service is available
|
|
android.hardware.dumpstate.IDumpstateDevice aidlService = getDumpstateDeviceAidlService();
|
|
if (aidlService != null) {
|
|
try {
|
|
return aidlService.getVerboseLoggingEnabled();
|
|
} catch (RemoteException re) {
|
|
if (DBG) Log.e(TAG, "aidlService.getVerboseLoggingEnabled fail: " + re);
|
|
}
|
|
}
|
|
|
|
// Then check HIDL v1.1 service
|
|
IDumpstateDevice service = getDumpstateDeviceService();
|
|
if (service == null || mDumpstateHalVersion < DUMPSTATE_HAL_VERSION_1_1) {
|
|
if (DBG) Log.d(TAG, "getVerboseLoggingEnabled not supported.");
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
android.hardware.dumpstate.V1_1.IDumpstateDevice service11 =
|
|
(android.hardware.dumpstate.V1_1.IDumpstateDevice) service;
|
|
if (service11 != null) {
|
|
return service11.getVerboseLoggingEnabled();
|
|
}
|
|
} catch (RemoteException | RuntimeException e) {
|
|
if (DBG) Log.e(TAG, "HIDL v1.1 getVerboseLoggingEnabled fail: " + e);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Return a {@IDumpstateDevice} instance or null if service is not available. */
|
|
@VisibleForTesting
|
|
@Nullable IDumpstateDevice getDumpstateDeviceService() {
|
|
IDumpstateDevice service = null;
|
|
try {
|
|
service = android.hardware.dumpstate.V1_1.IDumpstateDevice
|
|
.getService(true /* retry */);
|
|
mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_1_1;
|
|
} catch (NoSuchElementException | RemoteException e) {
|
|
if (DBG) Log.e(TAG, "Get HIDL v1.1 service fail: " + e);
|
|
}
|
|
|
|
if (service == null) {
|
|
try {
|
|
service = android.hardware.dumpstate.V1_0.IDumpstateDevice
|
|
.getService(true /* retry */);
|
|
mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_1_0;
|
|
} catch (NoSuchElementException | RemoteException e) {
|
|
if (DBG) Log.e(TAG, "Get HIDL v1.0 service fail: " + e);
|
|
}
|
|
}
|
|
|
|
if (service == null) {
|
|
mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_UNKNOWN;
|
|
}
|
|
return service;
|
|
}
|
|
|
|
/**
|
|
* Return a {@link android.hardware.dumpstate.IDumpstateDevice} instance or null if service is
|
|
* not available.
|
|
*/
|
|
@VisibleForTesting
|
|
@Nullable android.hardware.dumpstate.IDumpstateDevice getDumpstateDeviceAidlService() {
|
|
android.hardware.dumpstate.IDumpstateDevice service = null;
|
|
try {
|
|
service = android.hardware.dumpstate.IDumpstateDevice.Stub.asInterface(
|
|
ServiceManager.waitForDeclaredService(DUMP_STATE_AIDL_SERVICE_NAME));
|
|
} catch (NoSuchElementException e) {
|
|
if (DBG) Log.e(TAG, "Get AIDL service fail: " + e);
|
|
}
|
|
|
|
if (service != null) {
|
|
mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_2_0;
|
|
}
|
|
return service;
|
|
}
|
|
}
|