1 /* 2 * Copyright (C) 2020 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.settings.development; 18 19 import android.content.Context; 20 import android.hardware.dumpstate.V1_0.IDumpstateDevice; 21 import android.os.RemoteException; 22 import android.os.ServiceManager; 23 import android.util.Log; 24 25 import androidx.annotation.Nullable; 26 import androidx.annotation.VisibleForTesting; 27 import androidx.preference.Preference; 28 import androidx.preference.TwoStatePreference; 29 30 import com.android.settings.core.PreferenceControllerMixin; 31 import com.android.settingslib.development.DeveloperOptionsPreferenceController; 32 import com.android.settingslib.utils.ThreadUtils; 33 34 import java.util.NoSuchElementException; 35 36 public class EnableVerboseVendorLoggingPreferenceController 37 extends DeveloperOptionsPreferenceController 38 implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin { 39 40 private static final String TAG = "EnableVerboseVendorLoggingPreferenceController"; 41 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 42 43 private static final String ENABLE_VERBOSE_VENDOR_LOGGING_KEY = "enable_verbose_vendor_logging"; 44 private static final int DUMPSTATE_HAL_VERSION_UNKNOWN = -1; 45 private static final int DUMPSTATE_HAL_VERSION_1_0 = 0; // HIDL v1.0 46 private static final int DUMPSTATE_HAL_VERSION_1_1 = 1; // HIDL v1.1 47 private static final int DUMPSTATE_HAL_VERSION_2_0 = 2; // AIDL v1 48 49 private static final String DUMP_STATE_AIDL_SERVICE_NAME = 50 android.hardware.dumpstate.IDumpstateDevice.DESCRIPTOR + "/default"; 51 52 private int mDumpstateHalVersion; 53 EnableVerboseVendorLoggingPreferenceController(Context context)54 public EnableVerboseVendorLoggingPreferenceController(Context context) { 55 super(context); 56 mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_UNKNOWN; 57 } 58 59 @Override getPreferenceKey()60 public String getPreferenceKey() { 61 return ENABLE_VERBOSE_VENDOR_LOGGING_KEY; 62 } 63 64 @Override isAvailable()65 public boolean isAvailable() { 66 // Only show preference when IDumpstateDevice AIDL or HIDL v1.1 service is available 67 return isIDumpstateDeviceAidlServiceAvailable() || isIDumpstateDeviceV1_1ServiceAvailable(); 68 } 69 70 @SuppressWarnings("FutureReturnValueIgnored") 71 @Override onPreferenceChange(Preference preference, Object newValue)72 public boolean onPreferenceChange(Preference preference, Object newValue) { 73 final boolean isEnabled = (Boolean) newValue; 74 // IDumpstateDevice IPC may be blocking when system is extremely heavily-loaded. 75 // Post to background thread to avoid ANR. Ignore the returned Future. 76 ThreadUtils.postOnBackgroundThread(() -> 77 setVerboseLoggingEnabled(isEnabled)); 78 return true; 79 } 80 81 @SuppressWarnings("FutureReturnValueIgnored") 82 @Override updateState(Preference preference)83 public void updateState(Preference preference) { 84 ThreadUtils.postOnBackgroundThread(() -> { 85 final boolean enabled = getVerboseLoggingEnabled(); 86 ThreadUtils.getUiThreadHandler().post(() -> 87 ((TwoStatePreference) mPreference).setChecked(enabled)); 88 } 89 ); 90 } 91 92 @SuppressWarnings("FutureReturnValueIgnored") 93 @Override onDeveloperOptionsSwitchDisabled()94 protected void onDeveloperOptionsSwitchDisabled() { 95 super.onDeveloperOptionsSwitchDisabled(); 96 ThreadUtils.postOnBackgroundThread(() -> 97 setVerboseLoggingEnabled(false)); 98 ((TwoStatePreference) mPreference).setChecked(false); 99 } 100 101 @VisibleForTesting isIDumpstateDeviceV1_1ServiceAvailable()102 boolean isIDumpstateDeviceV1_1ServiceAvailable() { 103 IDumpstateDevice service = getDumpstateDeviceService(); 104 if (service == null) { 105 if (DBG) Log.d(TAG, "IDumpstateDevice v1.1 service is not available."); 106 } 107 return service != null && mDumpstateHalVersion == DUMPSTATE_HAL_VERSION_1_1; 108 } 109 110 @VisibleForTesting isIDumpstateDeviceAidlServiceAvailable()111 boolean isIDumpstateDeviceAidlServiceAvailable() { 112 android.hardware.dumpstate.IDumpstateDevice aidlService = getDumpstateDeviceAidlService(); 113 return aidlService != null; 114 } 115 116 @VisibleForTesting setVerboseLoggingEnabled(boolean enable)117 void setVerboseLoggingEnabled(boolean enable) { 118 // First check if AIDL service is available 119 android.hardware.dumpstate.IDumpstateDevice aidlService = getDumpstateDeviceAidlService(); 120 if (aidlService != null) { 121 try { 122 aidlService.setVerboseLoggingEnabled(enable); 123 } catch (RemoteException re) { 124 if (DBG) Log.e(TAG, "aidlService.setVerboseLoggingEnabled fail: " + re); 125 } 126 } 127 128 // Then check HIDL v1.1 service 129 IDumpstateDevice service = getDumpstateDeviceService(); 130 if (service == null || mDumpstateHalVersion < DUMPSTATE_HAL_VERSION_1_1) { 131 if (DBG) Log.d(TAG, "setVerboseLoggingEnabled not supported."); 132 return; 133 } 134 135 try { 136 android.hardware.dumpstate.V1_1.IDumpstateDevice service11 = 137 (android.hardware.dumpstate.V1_1.IDumpstateDevice) service; 138 if (service11 != null) { 139 service11.setVerboseLoggingEnabled(enable); 140 } 141 } catch (RemoteException | RuntimeException e) { 142 if (DBG) Log.e(TAG, "HIDL v1.1 setVerboseLoggingEnabled fail: " + e); 143 } 144 } 145 146 @VisibleForTesting getVerboseLoggingEnabled()147 boolean getVerboseLoggingEnabled() { 148 // First check if AIDL service is available 149 android.hardware.dumpstate.IDumpstateDevice aidlService = getDumpstateDeviceAidlService(); 150 if (aidlService != null) { 151 try { 152 return aidlService.getVerboseLoggingEnabled(); 153 } catch (RemoteException re) { 154 if (DBG) Log.e(TAG, "aidlService.getVerboseLoggingEnabled fail: " + re); 155 } 156 } 157 158 // Then check HIDL v1.1 service 159 IDumpstateDevice service = getDumpstateDeviceService(); 160 if (service == null || mDumpstateHalVersion < DUMPSTATE_HAL_VERSION_1_1) { 161 if (DBG) Log.d(TAG, "getVerboseLoggingEnabled not supported."); 162 return false; 163 } 164 165 try { 166 android.hardware.dumpstate.V1_1.IDumpstateDevice service11 = 167 (android.hardware.dumpstate.V1_1.IDumpstateDevice) service; 168 if (service11 != null) { 169 return service11.getVerboseLoggingEnabled(); 170 } 171 } catch (RemoteException | RuntimeException e) { 172 if (DBG) Log.e(TAG, "HIDL v1.1 getVerboseLoggingEnabled fail: " + e); 173 } 174 return false; 175 } 176 177 /** Return a {@IDumpstateDevice} instance or null if service is not available. */ 178 @VisibleForTesting getDumpstateDeviceService()179 @Nullable IDumpstateDevice getDumpstateDeviceService() { 180 IDumpstateDevice service = null; 181 try { 182 service = android.hardware.dumpstate.V1_1.IDumpstateDevice 183 .getService(true /* retry */); 184 mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_1_1; 185 } catch (NoSuchElementException | RemoteException e) { 186 if (DBG) Log.e(TAG, "Get HIDL v1.1 service fail: " + e); 187 } 188 189 if (service == null) { 190 try { 191 service = android.hardware.dumpstate.V1_0.IDumpstateDevice 192 .getService(true /* retry */); 193 mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_1_0; 194 } catch (NoSuchElementException | RemoteException e) { 195 if (DBG) Log.e(TAG, "Get HIDL v1.0 service fail: " + e); 196 } 197 } 198 199 if (service == null) { 200 mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_UNKNOWN; 201 } 202 return service; 203 } 204 205 /** 206 * Return a {@link android.hardware.dumpstate.IDumpstateDevice} instance or null if service is 207 * not available. 208 */ 209 @VisibleForTesting getDumpstateDeviceAidlService()210 @Nullable android.hardware.dumpstate.IDumpstateDevice getDumpstateDeviceAidlService() { 211 android.hardware.dumpstate.IDumpstateDevice service = null; 212 try { 213 service = android.hardware.dumpstate.IDumpstateDevice.Stub.asInterface( 214 ServiceManager.waitForDeclaredService(DUMP_STATE_AIDL_SERVICE_NAME)); 215 } catch (NoSuchElementException e) { 216 if (DBG) Log.e(TAG, "Get AIDL service fail: " + e); 217 } 218 219 if (service != null) { 220 mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_2_0; 221 } 222 return service; 223 } 224 } 225