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.server.gpu;
18 
19 import static android.content.Intent.ACTION_PACKAGE_ADDED;
20 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
21 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
22 
23 import android.annotation.NonNull;
24 import android.content.BroadcastReceiver;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageManager;
31 import android.database.ContentObserver;
32 import android.gamedriver.GameDriverProto.Blacklist;
33 import android.gamedriver.GameDriverProto.Blacklists;
34 import android.net.Uri;
35 import android.os.Build;
36 import android.os.Handler;
37 import android.os.SystemProperties;
38 import android.os.UserHandle;
39 import android.provider.DeviceConfig;
40 import android.provider.DeviceConfig.Properties;
41 import android.provider.Settings;
42 import android.util.Base64;
43 import android.util.Slog;
44 
45 import com.android.framework.protobuf.InvalidProtocolBufferException;
46 import com.android.internal.annotations.GuardedBy;
47 import com.android.server.SystemService;
48 
49 import java.io.BufferedReader;
50 import java.io.IOException;
51 import java.io.InputStreamReader;
52 import java.util.ArrayList;
53 import 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  */
61 public 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 
GpuService(Context context)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
onStart()100     public void onStart() {
101     }
102 
103     @Override
onBootPhase(int phase)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 
SettingsObserver()122         SettingsObserver() {
123             super(new Handler());
124             mContentResolver.registerContentObserver(mGameDriverBlackUri, false, this,
125                     UserHandle.USER_ALL);
126         }
127 
128         @Override
onChange(boolean selfChange, Uri uri)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 
DeviceConfigListener()143         DeviceConfigListener() {
144             super();
145             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_GAME_DRIVER,
146                     mContext.getMainExecutor(), this);
147         }
148         @Override
onPropertiesChanged(Properties properties)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
onReceive(@onNull final Context context, @NonNull final Intent intent)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 
assetToSettingsGlobal(Context context, Context driverContext, String fileName, String settingsGlobal, CharSequence delimiter)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 
fetchGameDriverPackageProperties()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 
processBlacklists()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 
parseBlacklists(String base64String)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 
setBlacklist()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 }
295