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 updatable 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 UPDATABLE_DRIVER_DEFAULT = 0;
57     public static final int UPDATABLE_DRIVER_PRODUCTION_ALL_APPS = 1;
58     public static final int UPDATABLE_DRIVER_PRERELEASE_ALL_APPS = 2;
59     public static final int UPDATABLE_DRIVER_OFF = 3;
60     public static final String PROPERTY_GFX_DRIVER_PRODUCTION = "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 mPreferenceProductionDriver;
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         mPreferenceProductionDriver =
83                 resources.getString(R.string.graphics_driver_app_preference_production_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.UPDATABLE_DRIVER_ALL_APPS,
96                                     UPDATABLE_DRIVER_DEFAULT)
97                                 != UPDATABLE_DRIVER_OFF)
98                 ? AVAILABLE
99                 : CONDITIONALLY_UNAVAILABLE;
100     }
101 
102     @Override
displayPreference(PreferenceScreen screen)103     public void displayPreference(PreferenceScreen screen) {
104         super.displayPreference(screen);
105         mPreference = screen.findPreference(getPreferenceKey());
106         mPreference.setEntries(mEntryList);
107         mPreference.setEntryValues(mEntryList);
108         mPreference.setOnPreferenceChangeListener(this);
109     }
110 
111     @Override
onStart()112     public void onStart() {
113         mGraphicsDriverContentObserver.register(mContentResolver);
114     }
115 
116     @Override
onStop()117     public void onStop() {
118         mGraphicsDriverContentObserver.unregister(mContentResolver);
119     }
120 
121     @Override
updateState(Preference preference)122     public void updateState(Preference preference) {
123         final ListPreference listPref = (ListPreference) preference;
124         listPref.setVisible(isAvailable());
125         final int currentChoice = Settings.Global.getInt(
126                 mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
127                 UPDATABLE_DRIVER_DEFAULT);
128         if (currentChoice == UPDATABLE_DRIVER_PRODUCTION_ALL_APPS) {
129             listPref.setValue(mPreferenceProductionDriver);
130             listPref.setSummary(mPreferenceProductionDriver);
131         } else if (currentChoice == UPDATABLE_DRIVER_PRERELEASE_ALL_APPS) {
132             listPref.setValue(mPreferencePrereleaseDriver);
133             listPref.setSummary(mPreferencePrereleaseDriver);
134         } else {
135             listPref.setValue(mPreferenceDefault);
136             listPref.setSummary(mPreferenceDefault);
137         }
138     }
139 
140     @Override
onPreferenceChange(Preference preference, Object newValue)141     public boolean onPreferenceChange(Preference preference, Object newValue) {
142         final ListPreference listPref = (ListPreference) preference;
143         final String value = newValue.toString();
144         final int currentChoice = Settings.Global.getInt(
145                 mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
146                 UPDATABLE_DRIVER_DEFAULT);
147         final int userChoice;
148         if (value.equals(mPreferenceProductionDriver)) {
149             userChoice = UPDATABLE_DRIVER_PRODUCTION_ALL_APPS;
150         } else if (value.equals(mPreferencePrereleaseDriver)) {
151             userChoice = UPDATABLE_DRIVER_PRERELEASE_ALL_APPS;
152         } else {
153             userChoice = UPDATABLE_DRIVER_DEFAULT;
154         }
155         listPref.setValue(value);
156         listPref.setSummary(value);
157 
158         if (userChoice != currentChoice) {
159             Settings.Global.putInt(
160                     mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS, userChoice);
161         }
162 
163         return true;
164     }
165 
166     @Override
onGraphicsDriverContentChanged()167     public void onGraphicsDriverContentChanged() {
168         updateState(mPreference);
169     }
170 
171     /**
172      * Constructs and returns a list of graphics driver choices.
173      */
constructEntryList(Context context, boolean withSystem)174     public static CharSequence[] constructEntryList(Context context, boolean withSystem) {
175         final Resources resources = context.getResources();
176         final String prereleaseDriverPackageName =
177                 SystemProperties.get(PROPERTY_GFX_DRIVER_PRERELEASE);
178         final String productionDriverPackageName =
179                 SystemProperties.get(PROPERTY_GFX_DRIVER_PRODUCTION);
180 
181         List<CharSequence> entryList = new ArrayList<>();
182         entryList.add(resources.getString(R.string.graphics_driver_app_preference_default));
183         final PackageManager pm = context.getPackageManager();
184         if (!TextUtils.isEmpty(prereleaseDriverPackageName)
185                 && hasDriverPackage(pm, prereleaseDriverPackageName)) {
186             entryList.add(resources.getString(
187                     R.string.graphics_driver_app_preference_prerelease_driver));
188         }
189         if (!TextUtils.isEmpty(productionDriverPackageName)
190                 && hasDriverPackage(pm, productionDriverPackageName)) {
191             entryList.add(resources.getString(
192                     R.string.graphics_driver_app_preference_production_driver));
193         }
194         if (withSystem) {
195             entryList.add(resources.getString(R.string.graphics_driver_app_preference_system));
196         }
197         CharSequence[] filteredEntryList = new CharSequence[entryList.size()];
198         filteredEntryList = entryList.toArray(filteredEntryList);
199         return filteredEntryList;
200     }
201 
hasDriverPackage(PackageManager pm, String driverPackageName)202     private static boolean hasDriverPackage(PackageManager pm, String driverPackageName) {
203         final ApplicationInfo driverAppInfo;
204         try {
205             driverAppInfo = pm.getApplicationInfo(driverPackageName,
206                     PackageManager.MATCH_SYSTEM_ONLY);
207         } catch (PackageManager.NameNotFoundException e) {
208             return false;
209         }
210         if (driverAppInfo.targetSdkVersion < Build.VERSION_CODES.O) {
211             return false;
212         }
213         final String abi = chooseAbi(driverAppInfo);
214         if (abi == null) {
215             return false;
216         }
217         return true;
218     }
219 
chooseAbi(ApplicationInfo ai)220     private static String chooseAbi(ApplicationInfo ai) {
221         final String isa = VMRuntime.getCurrentInstructionSet();
222         if (ai.primaryCpuAbi != null
223                 && isa.equals(VMRuntime.getInstructionSet(ai.primaryCpuAbi))) {
224             return ai.primaryCpuAbi;
225         }
226         if (ai.secondaryCpuAbi != null
227                 && isa.equals(VMRuntime.getInstructionSet(ai.secondaryCpuAbi))) {
228             return ai.secondaryCpuAbi;
229         }
230         return null;
231     }
232 }
233