blob: 955f17781540f68db5d2204d72cd6aed59d7d73b [file] [log] [blame]
Rahul Ravikumar05336002019-10-14 15:04:32 -07001/*
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
17package com.android.server.gpu;
18
19import static android.content.Intent.ACTION_PACKAGE_ADDED;
20import static android.content.Intent.ACTION_PACKAGE_CHANGED;
21import static android.content.Intent.ACTION_PACKAGE_REMOVED;
22
23import android.annotation.NonNull;
24import android.content.BroadcastReceiver;
25import android.content.ContentResolver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.ApplicationInfo;
30import android.content.pm.PackageManager;
31import android.database.ContentObserver;
32import android.gamedriver.GameDriverProto.Blacklist;
33import android.gamedriver.GameDriverProto.Blacklists;
34import android.net.Uri;
35import android.os.Build;
36import android.os.Handler;
37import android.os.SystemProperties;
38import android.os.UserHandle;
39import android.provider.DeviceConfig;
40import android.provider.DeviceConfig.Properties;
41import android.provider.Settings;
42import android.util.Base64;
43import android.util.Slog;
44
45import com.android.framework.protobuf.InvalidProtocolBufferException;
46import com.android.internal.annotations.GuardedBy;
47import com.android.server.SystemService;
48
49import java.io.BufferedReader;
50import java.io.IOException;
51import java.io.InputStreamReader;
52import java.util.ArrayList;
53import java.util.List;
54
55/**
56 * Service to manage GPU related features.
57 *
58 * <p>GPU service is a core service that monitors, coordinates all GPU related features,
59 * as well as collect metrics about the GPU and GPU driver.</p>
60 */
61public class GpuService extends SystemService {
62 public static final String TAG = "GpuService";
63 public static final boolean DEBUG = false;
64
65 private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
66 private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt";
67 private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
68
69 private final Context mContext;
70 private final String mDriverPackageName;
71 private final PackageManager mPackageManager;
72 private final Object mLock = new Object();
73 private final Object mDeviceConfigLock = new Object();
74 private ContentResolver mContentResolver;
75 private long mGameDriverVersionCode;
76 private SettingsObserver mSettingsObserver;
77 private DeviceConfigListener mDeviceConfigListener;
78 @GuardedBy("mLock")
79 private Blacklists mBlacklists;
80
81 public GpuService(Context context) {
82 super(context);
83
84 mContext = context;
85 mDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
86 mGameDriverVersionCode = -1;
87 mPackageManager = context.getPackageManager();
88 if (mDriverPackageName != null && !mDriverPackageName.isEmpty()) {
89 final IntentFilter packageFilter = new IntentFilter();
90 packageFilter.addAction(ACTION_PACKAGE_ADDED);
91 packageFilter.addAction(ACTION_PACKAGE_CHANGED);
92 packageFilter.addAction(ACTION_PACKAGE_REMOVED);
93 packageFilter.addDataScheme("package");
94 getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
95 packageFilter, null, null);
96 }
97 }
98
99 @Override
100 public void onStart() {
101 }
102
103 @Override
104 public void onBootPhase(int phase) {
105 if (phase == PHASE_BOOT_COMPLETED) {
106 mContentResolver = mContext.getContentResolver();
107 if (mDriverPackageName == null || mDriverPackageName.isEmpty()) {
108 return;
109 }
110 mSettingsObserver = new SettingsObserver();
111 mDeviceConfigListener = new DeviceConfigListener();
112 fetchGameDriverPackageProperties();
113 processBlacklists();
114 setBlacklist();
115 }
116 }
117
118 private final class SettingsObserver extends ContentObserver {
119 private final Uri mGameDriverBlackUri =
120 Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_BLACKLISTS);
121
122 SettingsObserver() {
123 super(new Handler());
124 mContentResolver.registerContentObserver(mGameDriverBlackUri, false, this,
125 UserHandle.USER_ALL);
126 }
127
128 @Override
129 public void onChange(boolean selfChange, Uri uri) {
130 if (uri == null) {
131 return;
132 }
133
134 if (mGameDriverBlackUri.equals(uri)) {
135 processBlacklists();
136 setBlacklist();
137 }
138 }
139 }
140
141 private final class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
142
143 DeviceConfigListener() {
144 super();
145 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_GAME_DRIVER,
146 mContext.getMainExecutor(), this);
147 }
148 @Override
149 public void onPropertiesChanged(Properties properties) {
150 synchronized (mDeviceConfigLock) {
151 if (properties.getKeyset().contains(Settings.Global.GAME_DRIVER_BLACKLISTS)) {
152 parseBlacklists(
153 properties.getString(Settings.Global.GAME_DRIVER_BLACKLISTS, ""));
154 setBlacklist();
155 }
156 }
157 }
158 }
159
160 private final class PackageReceiver extends BroadcastReceiver {
161 @Override
162 public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
163 final Uri data = intent.getData();
164 if (data == null && DEBUG) {
165 Slog.e(TAG, "Cannot handle package broadcast with null data");
166 return;
167 }
168 final String packageName = data.getSchemeSpecificPart();
169 if (!packageName.equals(mDriverPackageName)) {
170 return;
171 }
172
173 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
174
175 switch (intent.getAction()) {
176 case ACTION_PACKAGE_ADDED:
177 case ACTION_PACKAGE_CHANGED:
178 case ACTION_PACKAGE_REMOVED:
179 fetchGameDriverPackageProperties();
180 setBlacklist();
181 break;
182 default:
183 // do nothing
184 break;
185 }
186 }
187 }
188
189 private static void assetToSettingsGlobal(Context context, Context driverContext,
190 String fileName, String settingsGlobal, CharSequence delimiter) {
191 try {
192 final BufferedReader reader = new BufferedReader(
193 new InputStreamReader(driverContext.getAssets().open(fileName)));
194 final ArrayList<String> assetStrings = new ArrayList<>();
195 for (String assetString; (assetString = reader.readLine()) != null; ) {
196 assetStrings.add(assetString);
197 }
198 Settings.Global.putString(context.getContentResolver(),
199 settingsGlobal,
200 String.join(delimiter, assetStrings));
201 } catch (IOException e) {
202 if (DEBUG) {
203 Slog.w(TAG, "Failed to load " + fileName + ", abort.");
204 }
205 }
206 }
207
208 private void fetchGameDriverPackageProperties() {
209 final ApplicationInfo driverInfo;
210 try {
211 driverInfo = mPackageManager.getApplicationInfo(mDriverPackageName,
212 PackageManager.MATCH_SYSTEM_ONLY);
213 } catch (PackageManager.NameNotFoundException e) {
214 if (DEBUG) {
215 Slog.e(TAG, "driver package '" + mDriverPackageName + "' not installed");
216 }
217 return;
218 }
219
220 // O drivers are restricted to the sphal linker namespace, so don't try to use
221 // packages unless they declare they're compatible with that restriction.
222 if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
223 if (DEBUG) {
224 Slog.w(TAG, "Driver package is not known to be compatible with O");
225 }
226 return;
227 }
228
229 // Reset the whitelist.
230 Settings.Global.putString(mContentResolver,
231 Settings.Global.GAME_DRIVER_WHITELIST, "");
232 mGameDriverVersionCode = driverInfo.longVersionCode;
233
234 try {
235 final Context driverContext = mContext.createPackageContext(mDriverPackageName,
236 Context.CONTEXT_RESTRICTED);
237
238 assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_WHITELIST_FILENAME,
239 Settings.Global.GAME_DRIVER_WHITELIST, ",");
240 } catch (PackageManager.NameNotFoundException e) {
241 if (DEBUG) {
242 Slog.w(TAG, "driver package '" + mDriverPackageName + "' not installed");
243 }
244 }
245 }
246
247 private void processBlacklists() {
248 String base64String = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_GAME_DRIVER,
249 Settings.Global.GAME_DRIVER_BLACKLISTS);
250 if (base64String == null) {
251 base64String =
252 Settings.Global.getString(mContentResolver,
253 Settings.Global.GAME_DRIVER_BLACKLISTS);
254 }
255 parseBlacklists(base64String != null ? base64String : "");
256 }
257
258 private void parseBlacklists(String base64String) {
259 synchronized (mLock) {
260 // Reset all blacklists
261 mBlacklists = null;
262 try {
263 mBlacklists = Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS));
264 } catch (IllegalArgumentException e) {
265 if (DEBUG) {
266 Slog.w(TAG, "Can't parse blacklist, skip and continue...");
267 }
268 } catch (InvalidProtocolBufferException e) {
269 if (DEBUG) {
270 Slog.w(TAG, "Can't parse blacklist, skip and continue...");
271 }
272 }
273 }
274 }
275
276 private void setBlacklist() {
277 Settings.Global.putString(mContentResolver,
278 Settings.Global.GAME_DRIVER_BLACKLIST, "");
279 synchronized (mLock) {
280 if (mBlacklists == null) {
281 return;
282 }
283 List<Blacklist> blacklists = mBlacklists.getBlacklistsList();
284 for (Blacklist blacklist : blacklists) {
285 if (blacklist.getVersionCode() == mGameDriverVersionCode) {
286 Settings.Global.putString(mContentResolver,
287 Settings.Global.GAME_DRIVER_BLACKLIST,
288 String.join(",", blacklist.getPackageNamesList()));
289 return;
290 }
291 }
292 }
293 }
294}