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