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.text.TextUtils;
43 import android.util.Base64;
44 import android.util.Slog;
45 
46 import com.android.framework.protobuf.InvalidProtocolBufferException;
47 import com.android.internal.annotations.GuardedBy;
48 import com.android.server.SystemService;
49 
50 import java.io.BufferedReader;
51 import java.io.IOException;
52 import java.io.InputStreamReader;
53 import java.util.ArrayList;
54 import java.util.List;
55 
56 /**
57  * Service to manage GPU related features.
58  *
59  * <p>GPU service is a core service that monitors, coordinates all GPU related features,
60  * as well as collect metrics about the GPU and GPU driver.</p>
61  */
62 public class GpuService extends SystemService {
63     public static final String TAG = "GpuService";
64     public static final boolean DEBUG = false;
65 
66     private static final String PROD_DRIVER_PROPERTY = "ro.gfx.driver.0";
67     private static final String DEV_DRIVER_PROPERTY = "ro.gfx.driver.1";
68     private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt";
69     private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
70 
71     private final Context mContext;
72     private final String mProdDriverPackageName;
73     private final String mDevDriverPackageName;
74     private final PackageManager mPackageManager;
75     private final Object mLock = new Object();
76     private final Object mDeviceConfigLock = new Object();
77     private final boolean mHasProdDriver;
78     private final boolean mHasDevDriver;
79     private ContentResolver mContentResolver;
80     private long mGameDriverVersionCode;
81     private SettingsObserver mSettingsObserver;
82     private DeviceConfigListener mDeviceConfigListener;
83     @GuardedBy("mLock")
84     private Blacklists mBlacklists;
85 
GpuService(Context context)86     public GpuService(Context context) {
87         super(context);
88 
89         mContext = context;
90         mProdDriverPackageName = SystemProperties.get(PROD_DRIVER_PROPERTY);
91         mGameDriverVersionCode = -1;
92         mDevDriverPackageName = SystemProperties.get(DEV_DRIVER_PROPERTY);
93         mPackageManager = context.getPackageManager();
94         mHasProdDriver = !TextUtils.isEmpty(mProdDriverPackageName);
95         mHasDevDriver = !TextUtils.isEmpty(mDevDriverPackageName);
96         if (mHasDevDriver || mHasProdDriver) {
97             final IntentFilter packageFilter = new IntentFilter();
98             packageFilter.addAction(ACTION_PACKAGE_ADDED);
99             packageFilter.addAction(ACTION_PACKAGE_CHANGED);
100             packageFilter.addAction(ACTION_PACKAGE_REMOVED);
101             packageFilter.addDataScheme("package");
102             getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
103                                                 packageFilter, null, null);
104         }
105     }
106 
107     @Override
onStart()108     public void onStart() {
109     }
110 
111     @Override
onBootPhase(int phase)112     public void onBootPhase(int phase) {
113         if (phase == PHASE_BOOT_COMPLETED) {
114             mContentResolver = mContext.getContentResolver();
115             if (!mHasProdDriver && !mHasDevDriver) {
116                 return;
117             }
118             mSettingsObserver = new SettingsObserver();
119             mDeviceConfigListener = new DeviceConfigListener();
120             fetchGameDriverPackageProperties();
121             processBlacklists();
122             setBlacklist();
123             fetchDeveloperDriverPackageProperties();
124         }
125     }
126 
127     private final class SettingsObserver extends ContentObserver {
128         private final Uri mGameDriverBlackUri =
129                 Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_BLACKLISTS);
130 
SettingsObserver()131         SettingsObserver() {
132             super(new Handler());
133             mContentResolver.registerContentObserver(mGameDriverBlackUri, false, this,
134                     UserHandle.USER_ALL);
135         }
136 
137         @Override
onChange(boolean selfChange, Uri uri)138         public void onChange(boolean selfChange, Uri uri) {
139             if (uri == null) {
140                 return;
141             }
142 
143             if (mGameDriverBlackUri.equals(uri)) {
144                 processBlacklists();
145                 setBlacklist();
146             }
147         }
148     }
149 
150     private final class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
151 
DeviceConfigListener()152         DeviceConfigListener() {
153             super();
154             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_GAME_DRIVER,
155                     mContext.getMainExecutor(), this);
156         }
157         @Override
onPropertiesChanged(Properties properties)158         public void onPropertiesChanged(Properties properties) {
159             synchronized (mDeviceConfigLock) {
160                 if (properties.getKeyset().contains(Settings.Global.GAME_DRIVER_BLACKLISTS)) {
161                     parseBlacklists(
162                             properties.getString(Settings.Global.GAME_DRIVER_BLACKLISTS, ""));
163                     setBlacklist();
164                 }
165             }
166         }
167     }
168 
169     private final class PackageReceiver extends BroadcastReceiver {
170         @Override
onReceive(@onNull final Context context, @NonNull final Intent intent)171         public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
172             final Uri data = intent.getData();
173             if (data == null && DEBUG) {
174                 Slog.e(TAG, "Cannot handle package broadcast with null data");
175                 return;
176             }
177             final String packageName = data.getSchemeSpecificPart();
178             final boolean isProdDriver = packageName.equals(mProdDriverPackageName);
179             final boolean isDevDriver = packageName.equals(mDevDriverPackageName);
180             if (!isProdDriver && !isDevDriver) {
181                 return;
182             }
183 
184             switch (intent.getAction()) {
185                 case ACTION_PACKAGE_ADDED:
186                 case ACTION_PACKAGE_CHANGED:
187                 case ACTION_PACKAGE_REMOVED:
188                     if (isProdDriver) {
189                         fetchGameDriverPackageProperties();
190                         setBlacklist();
191                     } else if (isDevDriver) {
192                         fetchDeveloperDriverPackageProperties();
193                     }
194                     break;
195                 default:
196                     // do nothing
197                     break;
198             }
199         }
200     }
201 
assetToSettingsGlobal(Context context, Context driverContext, String fileName, String settingsGlobal, CharSequence delimiter)202     private static void assetToSettingsGlobal(Context context, Context driverContext,
203             String fileName, String settingsGlobal, CharSequence delimiter) {
204         try {
205             final BufferedReader reader = new BufferedReader(
206                     new InputStreamReader(driverContext.getAssets().open(fileName)));
207             final ArrayList<String> assetStrings = new ArrayList<>();
208             for (String assetString; (assetString = reader.readLine()) != null; ) {
209                 assetStrings.add(assetString);
210             }
211             Settings.Global.putString(context.getContentResolver(),
212                                       settingsGlobal,
213                                       String.join(delimiter, assetStrings));
214         } catch (IOException e) {
215             if (DEBUG) {
216                 Slog.w(TAG, "Failed to load " + fileName + ", abort.");
217             }
218         }
219     }
220 
fetchGameDriverPackageProperties()221     private void fetchGameDriverPackageProperties() {
222         final ApplicationInfo driverInfo;
223         try {
224             driverInfo = mPackageManager.getApplicationInfo(mProdDriverPackageName,
225                                                             PackageManager.MATCH_SYSTEM_ONLY);
226         } catch (PackageManager.NameNotFoundException e) {
227             if (DEBUG) {
228                 Slog.e(TAG, "driver package '" + mProdDriverPackageName + "' not installed");
229             }
230             return;
231         }
232 
233         // O drivers are restricted to the sphal linker namespace, so don't try to use
234         // packages unless they declare they're compatible with that restriction.
235         if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
236             if (DEBUG) {
237                 Slog.w(TAG, "Driver package is not known to be compatible with O");
238             }
239             return;
240         }
241 
242         // Reset the whitelist.
243         Settings.Global.putString(mContentResolver,
244                                   Settings.Global.GAME_DRIVER_WHITELIST, "");
245         mGameDriverVersionCode = driverInfo.longVersionCode;
246 
247         try {
248             final Context driverContext = mContext.createPackageContext(mProdDriverPackageName,
249                                                                         Context.CONTEXT_RESTRICTED);
250 
251             assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_WHITELIST_FILENAME,
252                     Settings.Global.GAME_DRIVER_WHITELIST, ",");
253         } catch (PackageManager.NameNotFoundException e) {
254             if (DEBUG) {
255                 Slog.w(TAG, "driver package '" + mProdDriverPackageName + "' not installed");
256             }
257         }
258     }
259 
processBlacklists()260     private void processBlacklists() {
261         String base64String = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_GAME_DRIVER,
262                 Settings.Global.GAME_DRIVER_BLACKLISTS);
263         if (base64String == null) {
264             base64String =
265                     Settings.Global.getString(mContentResolver,
266                                               Settings.Global.GAME_DRIVER_BLACKLISTS);
267         }
268         parseBlacklists(base64String != null ? base64String : "");
269     }
270 
parseBlacklists(String base64String)271     private void parseBlacklists(String base64String) {
272         synchronized (mLock) {
273             // Reset all blacklists
274             mBlacklists = null;
275             try {
276                 mBlacklists = Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS));
277             } catch (IllegalArgumentException e) {
278                 if (DEBUG) {
279                     Slog.w(TAG, "Can't parse blacklist, skip and continue...");
280                 }
281             } catch (InvalidProtocolBufferException e) {
282                 if (DEBUG) {
283                     Slog.w(TAG, "Can't parse blacklist, skip and continue...");
284                 }
285             }
286         }
287     }
288 
setBlacklist()289     private void setBlacklist() {
290         Settings.Global.putString(mContentResolver,
291                                   Settings.Global.GAME_DRIVER_BLACKLIST, "");
292         synchronized (mLock) {
293             if (mBlacklists == null) {
294                 return;
295             }
296             List<Blacklist> blacklists = mBlacklists.getBlacklistsList();
297             for (Blacklist blacklist : blacklists) {
298                 if (blacklist.getVersionCode() == mGameDriverVersionCode) {
299                     Settings.Global.putString(mContentResolver,
300                             Settings.Global.GAME_DRIVER_BLACKLIST,
301                             String.join(",", blacklist.getPackageNamesList()));
302                     return;
303                 }
304             }
305         }
306     }
307 
fetchDeveloperDriverPackageProperties()308     private void fetchDeveloperDriverPackageProperties() {
309         final ApplicationInfo driverInfo;
310         try {
311             driverInfo = mPackageManager.getApplicationInfo(mDevDriverPackageName,
312                                                             PackageManager.MATCH_SYSTEM_ONLY);
313         } catch (PackageManager.NameNotFoundException e) {
314             if (DEBUG) {
315                 Slog.e(TAG, "driver package '" + mDevDriverPackageName + "' not installed");
316             }
317             return;
318         }
319 
320         // O drivers are restricted to the sphal linker namespace, so don't try to use
321         // packages unless they declare they're compatible with that restriction.
322         if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
323             if (DEBUG) {
324                 Slog.w(TAG, "Driver package is not known to be compatible with O");
325             }
326             return;
327         }
328 
329         setUpdatableDriverPath(driverInfo);
330     }
331 
setUpdatableDriverPath(ApplicationInfo ai)332     private void setUpdatableDriverPath(ApplicationInfo ai) {
333         if (ai.primaryCpuAbi == null) {
334             nSetUpdatableDriverPath("");
335             return;
336         }
337         final StringBuilder sb = new StringBuilder();
338         sb.append(ai.sourceDir).append("!/lib/");
339         nSetUpdatableDriverPath(sb.toString());
340     }
341 
nSetUpdatableDriverPath(String driverPath)342     private static native void nSetUpdatableDriverPath(String driverPath);
343 }
344