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.graphicsdriver;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.PackageManager;
23 import android.content.res.Resources;
24 import android.os.Build;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.SystemProperties;
28 import android.provider.Settings;
29 import android.text.TextUtils;
30 
31 import androidx.annotation.VisibleForTesting;
32 import androidx.preference.ListPreference;
33 import androidx.preference.Preference;
34 import androidx.preference.PreferenceScreen;
35 
36 import com.android.settings.R;
37 import com.android.settings.core.BasePreferenceController;
38 import com.android.settingslib.core.lifecycle.LifecycleObserver;
39 import com.android.settingslib.core.lifecycle.events.OnStart;
40 import com.android.settingslib.core.lifecycle.events.OnStop;
41 import com.android.settingslib.development.DevelopmentSettingsEnabler;
42 
43 import dalvik.system.VMRuntime;
44 
45 import java.util.ArrayList;
46 import java.util.List;
47 
48 /**
49  * Controller of global switch to enable Game Driver for all Apps.
50  */
51 public class GraphicsDriverEnableForAllAppsPreferenceController extends BasePreferenceController
52         implements Preference.OnPreferenceChangeListener,
53                    GraphicsDriverContentObserver.OnGraphicsDriverContentChangedListener,
54                    LifecycleObserver, OnStart, OnStop {
55 
56     public static final int GAME_DRIVER_DEFAULT = 0;
57     public static final int GAME_DRIVER_ALL_APPS = 1;
58     public static final int GAME_DRIVER_PRERELEASE_ALL_APPS = 2;
59     public static final int GAME_DRIVER_OFF = 3;
60     public static final String PROPERTY_GFX_DRIVER_GAME = "ro.gfx.driver.0";
61     public static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1";
62 
63     private final Context mContext;
64     private final ContentResolver mContentResolver;
65     private final String mPreferenceDefault;
66     private final String mPreferenceGameDriver;
67     private final String mPreferencePrereleaseDriver;
68     @VisibleForTesting
69     CharSequence[] mEntryList;
70     @VisibleForTesting
71     GraphicsDriverContentObserver mGraphicsDriverContentObserver;
72 
73     private ListPreference mPreference;
74 
GraphicsDriverEnableForAllAppsPreferenceController(Context context, String key)75     public GraphicsDriverEnableForAllAppsPreferenceController(Context context, String key) {
76         super(context, key);
77         mContext = context;
78         mContentResolver = context.getContentResolver();
79 
80         final Resources resources = context.getResources();
81         mPreferenceDefault = resources.getString(R.string.graphics_driver_app_preference_default);
82         mPreferenceGameDriver =
83                 resources.getString(R.string.graphics_driver_app_preference_game_driver);
84         mPreferencePrereleaseDriver =
85                 resources.getString(R.string.graphics_driver_app_preference_prerelease_driver);
86         mEntryList = constructEntryList(mContext, false);
87         mGraphicsDriverContentObserver =
88                 new GraphicsDriverContentObserver(new Handler(Looper.getMainLooper()), this);
89     }
90 
91     @Override
getAvailabilityStatus()92     public int getAvailabilityStatus() {
93         return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)
94                         && (Settings.Global.getInt(mContentResolver,
95                                     Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT)
96                                 != GAME_DRIVER_OFF)
97                 ? AVAILABLE
98                 : CONDITIONALLY_UNAVAILABLE;
99     }
100 
101     @Override
displayPreference(PreferenceScreen screen)102     public void displayPreference(PreferenceScreen screen) {
103         super.displayPreference(screen);
104         mPreference = screen.findPreference(getPreferenceKey());
105         mPreference.setEntries(mEntryList);
106         mPreference.setEntryValues(mEntryList);
107         mPreference.setOnPreferenceChangeListener(this);
108     }
109 
110     @Override
onStart()111     public void onStart() {
112         mGraphicsDriverContentObserver.register(mContentResolver);
113     }
114 
115     @Override
onStop()116     public void onStop() {
117         mGraphicsDriverContentObserver.unregister(mContentResolver);
118     }
119 
120     @Override
updateState(Preference preference)121     public void updateState(Preference preference) {
122         final ListPreference listPref = (ListPreference) preference;
123         listPref.setVisible(isAvailable());
124         final int currentChoice = Settings.Global.getInt(
125                 mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT);
126         if (currentChoice == GAME_DRIVER_ALL_APPS) {
127             listPref.setValue(mPreferenceGameDriver);
128             listPref.setSummary(mPreferenceGameDriver);
129         } else if (currentChoice == GAME_DRIVER_PRERELEASE_ALL_APPS) {
130             listPref.setValue(mPreferencePrereleaseDriver);
131             listPref.setSummary(mPreferencePrereleaseDriver);
132         } else {
133             listPref.setValue(mPreferenceDefault);
134             listPref.setSummary(mPreferenceDefault);
135         }
136     }
137 
138     @Override
onPreferenceChange(Preference preference, Object newValue)139     public boolean onPreferenceChange(Preference preference, Object newValue) {
140         final ListPreference listPref = (ListPreference) preference;
141         final String value = newValue.toString();
142         final int currentChoice = Settings.Global.getInt(
143                 mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT);
144         final int userChoice;
145         if (value.equals(mPreferenceGameDriver)) {
146             userChoice = GAME_DRIVER_ALL_APPS;
147         } else if (value.equals(mPreferencePrereleaseDriver)) {
148             userChoice = GAME_DRIVER_PRERELEASE_ALL_APPS;
149         } else {
150             userChoice = GAME_DRIVER_DEFAULT;
151         }
152         listPref.setValue(value);
153         listPref.setSummary(value);
154 
155         if (userChoice != currentChoice) {
156             Settings.Global.putInt(
157                     mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS, userChoice);
158         }
159 
160         return true;
161     }
162 
163     @Override
onGraphicsDriverContentChanged()164     public void onGraphicsDriverContentChanged() {
165         updateState(mPreference);
166     }
167 
168     /**
169      * Constructs and returns a list of graphics driver choices.
170      */
constructEntryList(Context context, boolean withSystem)171     public static CharSequence[] constructEntryList(Context context, boolean withSystem) {
172         final Resources resources = context.getResources();
173         final String prereleaseDriverPackageName =
174                 SystemProperties.get(PROPERTY_GFX_DRIVER_PRERELEASE);
175         final String gameDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER_GAME);
176 
177         List<CharSequence> entryList = new ArrayList<>();
178         entryList.add(resources.getString(R.string.graphics_driver_app_preference_default));
179         final PackageManager pm = context.getPackageManager();
180         if (!TextUtils.isEmpty(prereleaseDriverPackageName)
181                 && hasDriverPackage(pm, prereleaseDriverPackageName)) {
182             entryList.add(resources.getString(
183                     R.string.graphics_driver_app_preference_prerelease_driver));
184         }
185         if (!TextUtils.isEmpty(gameDriverPackageName)
186                 && hasDriverPackage(pm, gameDriverPackageName)) {
187             entryList.add(resources.getString(R.string.graphics_driver_app_preference_game_driver));
188         }
189         if (withSystem) {
190             entryList.add(resources.getString(R.string.graphics_driver_app_preference_system));
191         }
192         CharSequence[] filteredEntryList = new CharSequence[entryList.size()];
193         filteredEntryList = entryList.toArray(filteredEntryList);
194         return filteredEntryList;
195     }
196 
hasDriverPackage(PackageManager pm, String driverPackageName)197     private static boolean hasDriverPackage(PackageManager pm, String driverPackageName) {
198         final ApplicationInfo driverAppInfo;
199         try {
200             driverAppInfo = pm.getApplicationInfo(driverPackageName,
201                     PackageManager.MATCH_SYSTEM_ONLY);
202         } catch (PackageManager.NameNotFoundException e) {
203             return false;
204         }
205         if (driverAppInfo.targetSdkVersion < Build.VERSION_CODES.O) {
206             return false;
207         }
208         final String abi = chooseAbi(driverAppInfo);
209         if (abi == null) {
210             return false;
211         }
212         return true;
213     }
214 
chooseAbi(ApplicationInfo ai)215     private static String chooseAbi(ApplicationInfo ai) {
216         final String isa = VMRuntime.getCurrentInstructionSet();
217         if (ai.primaryCpuAbi != null
218                 && isa.equals(VMRuntime.getInstructionSet(ai.primaryCpuAbi))) {
219             return ai.primaryCpuAbi;
220         }
221         if (ai.secondaryCpuAbi != null
222                 && isa.equals(VMRuntime.getInstructionSet(ai.secondaryCpuAbi))) {
223             return ai.secondaryCpuAbi;
224         }
225         return null;
226     }
227 }
228