Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright 2016 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 android.os;
     18 
     19 import android.content.Context;
     20 import android.content.pm.ApplicationInfo;
     21 import android.content.pm.PackageManager;
     22 import android.opengl.EGL14;
     23 import android.os.Build;
     24 import android.os.SystemProperties;
     25 import android.provider.Settings;
     26 import android.util.Log;
     27 
     28 import dalvik.system.VMRuntime;
     29 
     30 import java.io.File;
     31 
     32 /** @hide */
     33 public class GraphicsEnvironment {
     34 
     35     private static final GraphicsEnvironment sInstance = new GraphicsEnvironment();
     36 
     37     /**
     38      * Returns the shared {@link GraphicsEnvironment} instance.
     39      */
     40     public static GraphicsEnvironment getInstance() {
     41         return sInstance;
     42     }
     43 
     44     private static final boolean DEBUG = false;
     45     private static final String TAG = "GraphicsEnvironment";
     46     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
     47 
     48     private ClassLoader mClassLoader;
     49     private String mLayerPath;
     50     private String mDebugLayerPath;
     51 
     52     /**
     53      * Set up GraphicsEnvironment
     54      */
     55     public void setup(Context context) {
     56         setupGpuLayers(context);
     57         chooseDriver(context);
     58     }
     59 
     60     /**
     61      * Check whether application is debuggable
     62      */
     63     private static boolean isDebuggable(Context context) {
     64         return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) > 0;
     65     }
     66 
     67     /**
     68      * Store the layer paths available to the loader.
     69      */
     70     public void setLayerPaths(ClassLoader classLoader,
     71                               String layerPath,
     72                               String debugLayerPath) {
     73         // We have to store these in the class because they are set up before we
     74         // have access to the Context to properly set up GraphicsEnvironment
     75         mClassLoader = classLoader;
     76         mLayerPath = layerPath;
     77         mDebugLayerPath = debugLayerPath;
     78     }
     79 
     80     /**
     81      * Set up layer search paths for all apps
     82      * If debuggable, check for additional debug settings
     83      */
     84     private void setupGpuLayers(Context context) {
     85 
     86         String layerPaths = "";
     87 
     88         // Only enable additional debug functionality if the following conditions are met:
     89         // 1. App is debuggable
     90         // 2. ENABLE_GPU_DEBUG_LAYERS is true
     91         // 3. Package name is equal to GPU_DEBUG_APP
     92 
     93         if (isDebuggable(context)) {
     94 
     95             int enable = Settings.Global.getInt(context.getContentResolver(),
     96                                                 Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
     97 
     98             if (enable != 0) {
     99 
    100                 String gpuDebugApp = Settings.Global.getString(context.getContentResolver(),
    101                                                                Settings.Global.GPU_DEBUG_APP);
    102 
    103                 String packageName = context.getPackageName();
    104 
    105                 if ((gpuDebugApp != null && packageName != null)
    106                         && (!gpuDebugApp.isEmpty() && !packageName.isEmpty())
    107                         && gpuDebugApp.equals(packageName)) {
    108                     Log.i(TAG, "GPU debug layers enabled for " + packageName);
    109 
    110                     // Prepend the debug layer path as a searchable path.
    111                     // This will ensure debug layers added will take precedence over
    112                     // the layers specified by the app.
    113                     layerPaths = mDebugLayerPath + ":";
    114 
    115                     String layers = Settings.Global.getString(context.getContentResolver(),
    116                                                               Settings.Global.GPU_DEBUG_LAYERS);
    117 
    118                     Log.i(TAG, "Debug layer list: " + layers);
    119                     if (layers != null && !layers.isEmpty()) {
    120                         setDebugLayers(layers);
    121                     }
    122                 }
    123             }
    124 
    125         }
    126 
    127         // Include the app's lib directory in all cases
    128         layerPaths += mLayerPath;
    129 
    130         setLayerPaths(mClassLoader, layerPaths);
    131     }
    132 
    133     /**
    134      * Choose whether the current process should use the builtin or an updated driver.
    135      */
    136     private static void chooseDriver(Context context) {
    137         String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
    138         if (driverPackageName == null || driverPackageName.isEmpty()) {
    139             return;
    140         }
    141         // To minimize risk of driver updates crippling the device beyond user repair, never use an
    142         // updated driver for privileged or non-updated system apps. Presumably pre-installed apps
    143         // were tested thoroughly with the pre-installed driver.
    144         ApplicationInfo ai = context.getApplicationInfo();
    145         if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
    146             if (DEBUG) Log.v(TAG, "ignoring driver package for privileged/non-updated system app");
    147             return;
    148         }
    149         ApplicationInfo driverInfo;
    150         try {
    151             driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName,
    152                     PackageManager.MATCH_SYSTEM_ONLY);
    153         } catch (PackageManager.NameNotFoundException e) {
    154             Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
    155             return;
    156         }
    157         String abi = chooseAbi(driverInfo);
    158         if (abi == null) {
    159             if (DEBUG) {
    160                 // This is the normal case for the pre-installed empty driver package, don't spam
    161                 if (driverInfo.isUpdatedSystemApp()) {
    162                     Log.w(TAG, "updated driver package has no compatible native libraries");
    163                 }
    164             }
    165             return;
    166         }
    167         if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
    168             // O drivers are restricted to the sphal linker namespace, so don't try to use
    169             // packages unless they declare they're compatible with that restriction.
    170             Log.w(TAG, "updated driver package is not known to be compatible with O");
    171             return;
    172         }
    173 
    174         StringBuilder sb = new StringBuilder();
    175         sb.append(driverInfo.nativeLibraryDir)
    176           .append(File.pathSeparator);
    177         sb.append(driverInfo.sourceDir)
    178           .append("!/lib/")
    179           .append(abi);
    180         String paths = sb.toString();
    181 
    182         if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths);
    183         setDriverPath(paths);
    184     }
    185 
    186     /**
    187      * Start a background thread to initialize EGL.
    188      *
    189      * Initializing EGL involves loading and initializing the graphics driver. Some drivers take
    190      * several 10s of milliseconds to do this, so doing it on-demand when an app tries to render
    191      * its first frame adds directly to user-visible app launch latency. By starting it earlier
    192      * on a separate thread, it can usually be finished well before the UI is ready to be drawn.
    193      *
    194      * Should only be called after chooseDriver().
    195      */
    196     public static void earlyInitEGL() {
    197         Thread eglInitThread = new Thread(
    198                 () -> {
    199                     EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
    200                 },
    201                 "EGL Init");
    202         eglInitThread.start();
    203     }
    204 
    205     private static String chooseAbi(ApplicationInfo ai) {
    206         String isa = VMRuntime.getCurrentInstructionSet();
    207         if (ai.primaryCpuAbi != null &&
    208                 isa.equals(VMRuntime.getInstructionSet(ai.primaryCpuAbi))) {
    209             return ai.primaryCpuAbi;
    210         }
    211         if (ai.secondaryCpuAbi != null &&
    212                 isa.equals(VMRuntime.getInstructionSet(ai.secondaryCpuAbi))) {
    213             return ai.secondaryCpuAbi;
    214         }
    215         return null;
    216     }
    217 
    218     private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
    219     private static native void setDebugLayers(String layers);
    220     private static native void setDriverPath(String path);
    221 }
    222