1 /*
2  * Copyright 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.settings.development;
18 
19 import android.content.Context;
20 import android.os.IBinder;
21 import android.os.Parcel;
22 import android.os.RemoteException;
23 import android.os.ServiceManager;
24 
25 import androidx.annotation.VisibleForTesting;
26 import androidx.preference.Preference;
27 import androidx.preference.SwitchPreference;
28 
29 import com.android.settings.core.PreferenceControllerMixin;
30 import com.android.settingslib.development.DeveloperOptionsPreferenceController;
31 
32 /**
33  * Controller class for controlling the refresh rate overlay on SurfaceFlinger
34  */
35 public class ShowRefreshRatePreferenceController extends DeveloperOptionsPreferenceController
36         implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
37 
38     private static final String SHOW_REFRESH_RATE_KEY = "show_refresh_rate";
39 
40     private static final int SETTING_VALUE_QUERY = 2;
41     private static final int SETTING_VALUE_ON = 1;
42     private static final int SETTING_VALUE_OFF = 0;
43 
44     @VisibleForTesting
45     static final String SURFACE_FLINGER_SERVICE_KEY = "SurfaceFlinger";
46     @VisibleForTesting
47     static final int SURFACE_FLINGER_CODE = 1034;
48 
49     private static final String SURFACE_COMPOSER_INTERFACE_KEY = "android.ui.ISurfaceComposer";
50 
51     private final IBinder mSurfaceFlinger;
52 
ShowRefreshRatePreferenceController(Context context)53     public ShowRefreshRatePreferenceController(Context context) {
54         super(context);
55         mSurfaceFlinger = ServiceManager.getService(SURFACE_FLINGER_SERVICE_KEY);
56     }
57 
58     @Override
getPreferenceKey()59     public String getPreferenceKey() {
60         return SHOW_REFRESH_RATE_KEY;
61     }
62 
63     @Override
onPreferenceChange(Preference preference, Object newValue)64     public boolean onPreferenceChange(Preference preference, Object newValue) {
65         final boolean isEnabled = (Boolean) newValue;
66         writeShowRefreshRateSetting(isEnabled);
67         return true;
68     }
69 
70     @Override
updateState(Preference preference)71     public void updateState(Preference preference) {
72         updateShowRefreshRateSetting();
73     }
74 
75     @Override
onDeveloperOptionsSwitchDisabled()76     protected void onDeveloperOptionsSwitchDisabled() {
77         super.onDeveloperOptionsSwitchDisabled();
78         final SwitchPreference preference = (SwitchPreference) mPreference;
79         if (preference.isChecked()) {
80             // Writing false to the preference when the setting is already off will have a
81             // side effect of turning on the preference that we wish to avoid
82             writeShowRefreshRateSetting(false);
83             preference.setChecked(false);
84         }
85     }
86 
87     @VisibleForTesting
updateShowRefreshRateSetting()88     void updateShowRefreshRateSetting() {
89         // magic communication with surface flinger.
90         try {
91             if (mSurfaceFlinger != null) {
92                 final Parcel data = Parcel.obtain();
93                 final Parcel reply = Parcel.obtain();
94                 data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
95                 data.writeInt(SETTING_VALUE_QUERY);
96                 mSurfaceFlinger.transact(SURFACE_FLINGER_CODE, data, reply, 0 /* flags */);
97                 final boolean enabled = reply.readBoolean();
98                 ((SwitchPreference) mPreference).setChecked(enabled);
99                 reply.recycle();
100                 data.recycle();
101             }
102         } catch (RemoteException ex) {
103             // intentional no-op
104         }
105     }
106 
107     @VisibleForTesting
writeShowRefreshRateSetting(boolean isEnabled)108     void writeShowRefreshRateSetting(boolean isEnabled) {
109         try {
110             if (mSurfaceFlinger != null) {
111                 final Parcel data = Parcel.obtain();
112                 data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
113                 final int showRefreshRate = isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF;
114                 data.writeInt(showRefreshRate);
115                 mSurfaceFlinger.transact(SURFACE_FLINGER_CODE, data,
116                         null /* reply */, 0 /* flags */);
117                 data.recycle();
118             }
119         } catch (RemoteException ex) {
120             // intentional no-op
121         }
122         updateShowRefreshRateSetting();
123     }
124 }
125