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