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