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