• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.graphicsdriver;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.os.GraphicsEnvironment;
22 import android.os.SystemProperties;
23 import android.text.TextUtils;
24 import android.util.Log;
25 
26 import androidx.annotation.VisibleForTesting;
27 import androidx.preference.Preference;
28 import androidx.preference.SwitchPreference;
29 
30 import com.android.settings.R;
31 import com.android.settings.core.PreferenceControllerMixin;
32 import com.android.settings.development.DevelopmentSettingsDashboardFragment;
33 import com.android.settings.development.RebootConfirmationDialogFragment;
34 import com.android.settings.development.RebootConfirmationDialogHost;
35 import com.android.settingslib.development.DeveloperOptionsPreferenceController;
36 
37 /** Controller to handle the events when user toggles this developer option switch: Enable ANGLE */
38 public class GraphicsDriverEnableAngleAsSystemDriverController
39         extends DeveloperOptionsPreferenceController
40         implements Preference.OnPreferenceChangeListener,
41                 PreferenceControllerMixin,
42                 RebootConfirmationDialogHost {
43 
44     private static final String TAG = "GraphicsEnableAngleCtrl";
45 
46     private static final String ENABLE_ANELE_AS_SYSTEM_DRIVER_KEY = "enable_angle_as_system_driver";
47 
48     private final DevelopmentSettingsDashboardFragment mFragment;
49 
50     private final GraphicsDriverSystemPropertiesWrapper mSystemProperties;
51 
52     private boolean mShouldToggleSwitchBackOnRebootDialogDismiss;
53 
54     @VisibleForTesting
55     static final String PROPERTY_RO_GFX_ANGLE_SUPPORTED = "ro.gfx.angle.supported";
56 
57     @VisibleForTesting
58     static final String PROPERTY_PERSISTENT_GRAPHICS_EGL = "persist.graphics.egl";
59 
60     @VisibleForTesting
61     static final String PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION =
62             "debug.graphics.angle.developeroption.enable";
63 
64     @VisibleForTesting static final String ANGLE_DRIVER_SUFFIX = "angle";
65 
66     @VisibleForTesting
67     static class Injector {
createSystemPropertiesWrapper()68         public GraphicsDriverSystemPropertiesWrapper createSystemPropertiesWrapper() {
69             return new GraphicsDriverSystemPropertiesWrapper() {
70                 @Override
71                 public String get(String key, String def) {
72                     return SystemProperties.get(key, def);
73                 }
74 
75                 @Override
76                 public void set(String key, String val) {
77                     SystemProperties.set(key, val);
78                 }
79 
80                 @Override
81                 public boolean getBoolean(String key, boolean def) {
82                     return SystemProperties.getBoolean(key, def);
83                 }
84             };
85         }
86     }
87 
88     public GraphicsDriverEnableAngleAsSystemDriverController(
89             Context context, DevelopmentSettingsDashboardFragment fragment) {
90         this(context, fragment, new Injector());
91     }
92 
93     // Return true if the ANGLE developer option entry point is enabled.
94     // This can be enabled by calling:
95     //     `adb shell setprop debug.graphics.angle.developeroption.enable true`
96     private boolean isAngleDeveloperOptionEnabled() {
97         return mSystemProperties.getBoolean(PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION, false);
98     }
99 
100     private boolean isAngleSupported() {
101         return TextUtils.equals(
102                         mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true");
103     }
104 
105     @VisibleForTesting
106     GraphicsDriverEnableAngleAsSystemDriverController(
107             Context context, DevelopmentSettingsDashboardFragment fragment, Injector injector) {
108         super(context);
109         mFragment = fragment;
110         mSystemProperties = injector.createSystemPropertiesWrapper();
111         // By default, when the reboot dialog is dismissed we want to toggle the switch back.
112         // Exception is when user chooses to reboot now, the switch should keep its current value
113         // and persist its' state over reboot.
114         mShouldToggleSwitchBackOnRebootDialogDismiss = true;
115         final String persistGraphicsEglValue =
116                 mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
117         Log.v(TAG, "Value of " + PROPERTY_PERSISTENT_GRAPHICS_EGL + " is: "
118                 + persistGraphicsEglValue);
119     }
120 
121     @Override
122     public String getPreferenceKey() {
123         return ENABLE_ANELE_AS_SYSTEM_DRIVER_KEY;
124     }
125 
126     @Override
127     public boolean onPreferenceChange(Preference preference, Object newValue) {
128         final boolean enableAngleAsSystemDriver = (Boolean) newValue;
129         // set "persist.graphics.egl" to "angle" if enableAngleAsSystemDriver is true
130         // set "persist.graphics.egl" to "" if enableAngleAsSystemDriver is false
131         GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(enableAngleAsSystemDriver);
132         // pop up a window asking user to reboot to make the new "persist.graphics.egl" take effect
133         showRebootDialog();
134         return true;
135     }
136 
137     @VisibleForTesting
138     void showRebootDialog() {
139         RebootConfirmationDialogFragment.show(
140                 mFragment,
141                 R.string.reboot_dialog_enable_angle_as_system_driver,
142                 R.string.cancel,
143                 this);
144     }
145 
146     /** Return the default value of "persist.graphics.egl" */
147     public boolean isDefaultValue() {
148         if (!isAngleSupported()) {
149             return true;
150         }
151 
152         final String currentGlesDriver =
153                 mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
154         // default value of "persist.graphics.egl" is ""
155         return TextUtils.isEmpty(currentGlesDriver);
156     }
157 
158     @Override
159     public void updateState(Preference preference) {
160         super.updateState(preference);
161         if (isAngleSupported()) {
162             // set switch on if "persist.graphics.egl" is "angle" and angle is built in /vendor
163             // set switch off otherwise.
164             final String currentGlesDriver =
165                     mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
166             final boolean isAngle = TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver);
167             ((SwitchPreference) mPreference).setChecked(isAngle);
168         } else {
169             mPreference.setEnabled(false);
170             ((SwitchPreference) mPreference).setChecked(false);
171         }
172 
173         // Regardless of whether ANGLE is enabled, disable the developer option UI
174         // as long as UI is not enabled via debug property.
175         if (!isAngleDeveloperOptionEnabled()) {
176             mPreference.setEnabled(false);
177         }
178     }
179 
180     @Override
181     protected void onDeveloperOptionsSwitchDisabled() {
182         // 1) disable the switch
183         super.onDeveloperOptionsSwitchDisabled();
184         if (isAngleSupported()) {
185             // 2) set the persist.graphics.egl empty string
186             GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false);
187             // 3) reset the switch
188             ((SwitchPreference) mPreference).setChecked(false);
189         }
190     }
191 
192     void toggleSwitchBack() {
193         final String currentGlesDriver =
194                 mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
195         if (TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver)) {
196             // if persist.graphics.egl = "angle", set the property value back to ""
197             GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false);
198             // toggle switch off
199             ((SwitchPreference) mPreference).setChecked(false);
200             return;
201         }
202 
203         if (TextUtils.isEmpty(currentGlesDriver)) {
204             // if persist.graphicx.egl = "", set the persist.graphics.egl back to "angle"
205             GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(true);
206             // toggle switch on
207             ((SwitchPreference) mPreference).setChecked(true);
208             return;
209         }
210 
211         // if persist.graphics.egl holds values other than the above two, log error message
212         Log.e(TAG, "Invalid persist.graphics.egl property value");
213     }
214 
215     @VisibleForTesting
216     void rebootDevice(Context context) {
217         final Intent intent = new Intent(Intent.ACTION_REBOOT);
218         context.startActivity(intent);
219     }
220 
221     @Override
222     public void onRebootConfirmed(Context context) {
223         // User chooses to reboot now, do not toggle switch back
224         mShouldToggleSwitchBackOnRebootDialogDismiss = false;
225 
226         // Reboot the device
227         rebootDevice(context);
228     }
229 
230     @Override
231     public void onRebootCancelled() {
232         // User chooses to cancel reboot, toggle switch back
233         mShouldToggleSwitchBackOnRebootDialogDismiss = true;
234     }
235 
236     @Override
237     public void onRebootDialogDismissed() {
238         // If reboot dialog is dismissed either from
239         // 1) User clicks cancel
240         // 2) User taps phone screen area outside of reboot dialog
241         // do not reboot the device, and toggles switch back.
242         if (mShouldToggleSwitchBackOnRebootDialogDismiss) {
243             toggleSwitchBack();
244         }
245 
246         // Reset the flag so that the default option is to toggle switch back
247         // on reboot dialog dismissed.
248         mShouldToggleSwitchBackOnRebootDialogDismiss = true;
249     }
250 }
251