Home | History | Annotate | Download | only in am
      1 /*
      2  * Copyright (C) 2018 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.server.am;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.KeyguardManager;
     21 import android.content.Context;
     22 import android.content.nano.DeviceConfigurationProto;
     23 import android.content.nano.GlobalConfigurationProto;
     24 import android.content.nano.LocaleProto;
     25 import android.content.nano.ResourcesConfigurationProto;
     26 import android.content.pm.ConfigurationInfo;
     27 import android.content.pm.FeatureInfo;
     28 import android.content.pm.PackageManager;
     29 import android.content.pm.SharedLibraryInfo;
     30 import android.content.res.Configuration;
     31 import android.graphics.Point;
     32 import android.hardware.display.DisplayManager;
     33 import android.opengl.GLES10;
     34 import android.os.Build;
     35 import android.os.LocaleList;
     36 import android.os.ParcelFileDescriptor;
     37 import android.support.test.InstrumentationRegistry;
     38 import android.text.TextUtils;
     39 import android.util.DisplayMetrics;
     40 import android.view.Display;
     41 
     42 import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
     43 
     44 import java.io.ByteArrayOutputStream;
     45 import java.io.FileInputStream;
     46 import java.io.IOException;
     47 import java.nio.charset.StandardCharsets;
     48 import java.util.Arrays;
     49 import java.util.Collections;
     50 import java.util.Comparator;
     51 import java.util.HashSet;
     52 import java.util.List;
     53 import java.util.Set;
     54 
     55 import javax.microedition.khronos.egl.EGL10;
     56 import javax.microedition.khronos.egl.EGLConfig;
     57 import javax.microedition.khronos.egl.EGLContext;
     58 import javax.microedition.khronos.egl.EGLDisplay;
     59 import javax.microedition.khronos.egl.EGLSurface;
     60 
     61 import org.junit.Before;
     62 import org.junit.Test;
     63 
     64 import static org.junit.Assert.assertArrayEquals;
     65 import static org.junit.Assert.assertEquals;
     66 
     67 public class ActivityManagerGetConfigTests {
     68     Context mContext;
     69     ActivityManager mAm;
     70     PackageManager mPm;
     71 
     72     @Before
     73     public void setUp() throws Exception {
     74         mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
     75         mAm = mContext.getSystemService(ActivityManager.class);
     76         mPm = mContext.getPackageManager();
     77     }
     78 
     79     private byte[] executeShellCommand(String cmd) {
     80         try {
     81             ParcelFileDescriptor pfd =
     82                     InstrumentationRegistry.getInstrumentation().getUiAutomation()
     83                             .executeShellCommand(cmd);
     84             byte[] buf = new byte[512];
     85             int bytesRead;
     86             FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
     87             ByteArrayOutputStream stdout = new ByteArrayOutputStream();
     88             while ((bytesRead = fis.read(buf)) != -1) {
     89                 stdout.write(buf, 0, bytesRead);
     90             }
     91             fis.close();
     92             return stdout.toByteArray();
     93         } catch (IOException e) {
     94             throw new RuntimeException(e);
     95         }
     96     }
     97 
     98     /**
     99      * Adds all supported GL extensions for a provided EGLConfig to a set by creating an EGLContext
    100      * and EGLSurface and querying extensions.
    101      *
    102      * @param egl An EGL API object
    103      * @param display An EGLDisplay to create a context and surface with
    104      * @param config The EGLConfig to get the extensions for
    105      * @param surfaceSize eglCreatePbufferSurface generic parameters
    106      * @param contextAttribs eglCreateContext generic parameters
    107      * @param glExtensions A Set<String> to add GL extensions to
    108      */
    109     private static void addExtensionsForConfig(
    110             EGL10 egl,
    111             EGLDisplay display,
    112             EGLConfig config,
    113             int[] surfaceSize,
    114             int[] contextAttribs,
    115             Set<String> glExtensions) {
    116         // Create a context.
    117         EGLContext context =
    118                 egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, contextAttribs);
    119         // No-op if we can't create a context.
    120         if (context == EGL10.EGL_NO_CONTEXT) {
    121             return;
    122         }
    123 
    124         // Create a surface.
    125         EGLSurface surface = egl.eglCreatePbufferSurface(display, config, surfaceSize);
    126         if (surface == EGL10.EGL_NO_SURFACE) {
    127             egl.eglDestroyContext(display, context);
    128             return;
    129         }
    130 
    131         // Update the current surface and context.
    132         egl.eglMakeCurrent(display, surface, surface, context);
    133 
    134         // Get the list of extensions.
    135         String extensionList = GLES10.glGetString(GLES10.GL_EXTENSIONS);
    136         if (!TextUtils.isEmpty(extensionList)) {
    137             // The list of extensions comes from the driver separated by spaces.
    138             // Split them apart and add them into a Set for deduping purposes.
    139             for (String extension : extensionList.split(" ")) {
    140                 glExtensions.add(extension);
    141             }
    142         }
    143 
    144         // Tear down the context and surface for this config.
    145         egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
    146         egl.eglDestroySurface(display, surface);
    147         egl.eglDestroyContext(display, context);
    148     }
    149 
    150 
    151     Set<String> getGlExtensionsFromDriver() {
    152         Set<String> glExtensions = new HashSet<>();
    153 
    154         // Get the EGL implementation.
    155         EGL10 egl = (EGL10) EGLContext.getEGL();
    156         if (egl == null) {
    157             throw new RuntimeException("Warning: couldn't get EGL");
    158         }
    159 
    160         // Get the default display and initialize it.
    161         EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    162         int[] version = new int[2];
    163         egl.eglInitialize(display, version);
    164 
    165         // Call getConfigs() in order to find out how many there are.
    166         int[] numConfigs = new int[1];
    167         if (!egl.eglGetConfigs(display, null, 0, numConfigs)) {
    168             throw new RuntimeException("Warning: couldn't get EGL config count");
    169         }
    170 
    171         // Allocate space for all configs and ask again.
    172         EGLConfig[] configs = new EGLConfig[numConfigs[0]];
    173         if (!egl.eglGetConfigs(display, configs, numConfigs[0], numConfigs)) {
    174             throw new RuntimeException("Warning: couldn't get EGL configs");
    175         }
    176 
    177         // Allocate surface size parameters outside of the main loop to cut down
    178         // on GC thrashing.  1x1 is enough since we are only using it to get at
    179         // the list of extensions.
    180         int[] surfaceSize =
    181                 new int[] {
    182                         EGL10.EGL_WIDTH, 1,
    183                         EGL10.EGL_HEIGHT, 1,
    184                         EGL10.EGL_NONE
    185                 };
    186 
    187         // For when we need to create a GLES2.0 context.
    188         final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
    189         int[] gles2 = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
    190 
    191         // For getting return values from eglGetConfigAttrib
    192         int[] attrib = new int[1];
    193 
    194         for (int i = 0; i < numConfigs[0]; i++) {
    195             // Get caveat for this config in order to skip slow (i.e. software) configs.
    196             egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_CONFIG_CAVEAT, attrib);
    197             if (attrib[0] == EGL10.EGL_SLOW_CONFIG) {
    198                 continue;
    199             }
    200 
    201             // If the config does not support pbuffers we cannot do an eglMakeCurrent
    202             // on it in addExtensionsForConfig(), so skip it here. Attempting to make
    203             // it current with a pbuffer will result in an EGL_BAD_MATCH error
    204             egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_SURFACE_TYPE, attrib);
    205             if ((attrib[0] & EGL10.EGL_PBUFFER_BIT) == 0) {
    206                 continue;
    207             }
    208 
    209             final int EGL_OPENGL_ES_BIT = 0x0001;
    210             final int EGL_OPENGL_ES2_BIT = 0x0004;
    211             egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_RENDERABLE_TYPE, attrib);
    212             if ((attrib[0] & EGL_OPENGL_ES_BIT) != 0) {
    213                 addExtensionsForConfig(egl, display, configs[i], surfaceSize, null, glExtensions);
    214             }
    215             if ((attrib[0] & EGL_OPENGL_ES2_BIT) != 0) {
    216                 addExtensionsForConfig(egl, display, configs[i], surfaceSize, gles2, glExtensions);
    217             }
    218         }
    219 
    220         // Release all EGL resources.
    221         egl.eglTerminate(display);
    222 
    223         return glExtensions;
    224     }
    225 
    226     private void checkResourceConfig(Configuration config, DisplayMetrics metrics,
    227             ResourcesConfigurationProto resConfig) {
    228         final int width, height;
    229         if (metrics.widthPixels >= metrics.heightPixels) {
    230             width = metrics.widthPixels;
    231             height = metrics.heightPixels;
    232         } else {
    233             //noinspection SuspiciousNameCombination
    234             width = metrics.heightPixels;
    235             //noinspection SuspiciousNameCombination
    236             height = metrics.widthPixels;
    237         }
    238 
    239         assertEquals("Expected SDK version does not match",
    240                 Build.VERSION.RESOURCES_SDK_INT, resConfig.sdkVersion);
    241         assertEquals("Expected screen width px does not match",
    242                 width, resConfig.screenWidthPx);
    243         assertEquals("Expected screen width px does not match",
    244                 height, resConfig.screenHeightPx);
    245 
    246         assertEquals("Expected font scale does not match",
    247                 config.fontScale, resConfig.configuration.fontScale, Float.MIN_VALUE*5);
    248         assertEquals("Expected mcc does not match",
    249                 config.mcc, resConfig.configuration.mcc);
    250         assertEquals("Expected mnc does not match",
    251                 config.mnc, resConfig.configuration.mnc);
    252         LocaleList llist = config.getLocales();
    253         LocaleProto[] lprotos = resConfig.configuration.locales;
    254         assertEquals("Expected number of locales does not match",
    255                 llist.size(), lprotos.length);
    256         for (int i = 0; i < llist.size(); i++) {
    257             assertEquals("Expected locale #" + i + " language does not match",
    258                     llist.get(i).getLanguage(), lprotos[i].language);
    259             assertEquals("Expected locale #" + i + " country does not match",
    260                     llist.get(i).getCountry(), lprotos[i].country);
    261             assertEquals("Expected locale #" + i + " variant does not match",
    262                     llist.get(i).getVariant(), lprotos[i].variant);
    263         }
    264         assertEquals("Expected screen layout does not match",
    265                 config.screenLayout, resConfig.configuration.screenLayout);
    266         assertEquals("Expected color mode does not match",
    267                 config.colorMode, resConfig.configuration.colorMode);
    268         assertEquals("Expected touchscreen does not match",
    269                 config.touchscreen, resConfig.configuration.touchscreen);
    270         assertEquals("Expected keyboard does not match",
    271                 config.keyboard, resConfig.configuration.keyboard);
    272         assertEquals("Expected keyboard hidden does not match",
    273                 config.keyboardHidden, resConfig.configuration.keyboardHidden);
    274         assertEquals("Expected hard keyboard hidden does not match",
    275                 config.hardKeyboardHidden, resConfig.configuration.hardKeyboardHidden);
    276         assertEquals("Expected navigation does not match",
    277                 config.navigation, resConfig.configuration.navigation);
    278         assertEquals("Expected navigation hidden does not match",
    279                 config.navigationHidden, resConfig.configuration.navigationHidden);
    280         assertEquals("Expected orientation does not match",
    281                 config.orientation, resConfig.configuration.orientation);
    282         assertEquals("Expected UI mode does not match",
    283                 config.uiMode, resConfig.configuration.uiMode);
    284         assertEquals("Expected screen width dp does not match",
    285                 config.screenWidthDp, resConfig.configuration.screenWidthDp);
    286         assertEquals("Expected screen hight dp does not match",
    287                 config.screenHeightDp, resConfig.configuration.screenHeightDp);
    288         assertEquals("Expected smallest screen width dp does not match",
    289                 config.smallestScreenWidthDp, resConfig.configuration.smallestScreenWidthDp);
    290         assertEquals("Expected density dpi does not match",
    291                 config.densityDpi, resConfig.configuration.densityDpi);
    292         // XXX not comparing windowConfiguration, since by definition this is contextual.
    293     }
    294 
    295     private void checkDeviceConfig(DisplayManager dm, DeviceConfigurationProto deviceConfig) {
    296         Point stableSize = dm.getStableDisplaySize();
    297         assertEquals("Expected stable screen width does not match",
    298                 stableSize.x, deviceConfig.stableScreenWidthPx);
    299         assertEquals("Expected stable screen height does not match",
    300                 stableSize.y, deviceConfig.stableScreenHeightPx);
    301         assertEquals("Expected stable screen density does not match",
    302                 DisplayMetrics.DENSITY_DEVICE_STABLE, deviceConfig.stableDensityDpi);
    303 
    304         assertEquals("Expected total RAM does not match",
    305                 mAm.getTotalRam(), deviceConfig.totalRam);
    306         assertEquals("Expected low RAM does not match",
    307                 mAm.isLowRamDevice(), deviceConfig.lowRam);
    308         assertEquals("Expected max cores does not match",
    309                 Runtime.getRuntime().availableProcessors(), deviceConfig.maxCores);
    310         KeyguardManager kgm = mContext.getSystemService(KeyguardManager.class);
    311         assertEquals("Expected has secure screen lock does not match",
    312                 kgm.isDeviceSecure(), deviceConfig.hasSecureScreenLock);
    313 
    314         ConfigurationInfo configInfo = mAm.getDeviceConfigurationInfo();
    315         if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
    316             assertEquals("Expected opengl version does not match",
    317                     configInfo.reqGlEsVersion, deviceConfig.openglVersion);
    318         }
    319 
    320         Set<String> glExtensionsSet = getGlExtensionsFromDriver();
    321         String[] glExtensions = new String[glExtensionsSet.size()];
    322         glExtensions = glExtensionsSet.toArray(glExtensions);
    323         Arrays.sort(glExtensions);
    324         assertArrayEquals("Expected opengl extensions does not match",
    325                 glExtensions, deviceConfig.openglExtensions);
    326 
    327         List<SharedLibraryInfo> slibs = mPm.getSharedLibraries(0);
    328         Collections.sort(slibs, Comparator.comparing(SharedLibraryInfo::getName));
    329         String[] slibNames = new String[slibs.size()];
    330         for (int i = 0; i < slibs.size(); i++) {
    331             slibNames[i] = slibs.get(i).getName();
    332         }
    333         assertArrayEquals("Expected shared libraries does not match",
    334                 slibNames, deviceConfig.sharedLibraries);
    335 
    336         FeatureInfo[] features = mPm.getSystemAvailableFeatures();
    337         Arrays.sort(features, (o1, o2) ->
    338                 (o1.name == o2.name ? 0 : (o1.name == null ? -1 : o1.name.compareTo(o2.name))));
    339         int size = 0;
    340         for (int i = 0; i < features.length; i++) {
    341             if (features[i].name != null) {
    342                 size++;
    343             }
    344         }
    345         String[] featureNames = new String[size];
    346         for (int i = 0, j = 0; i < features.length; i++) {
    347             if (features[i].name != null) {
    348                 featureNames[j] = features[i].name;
    349                 j++;
    350             }
    351         }
    352         assertArrayEquals("Expected features does not match",
    353                 featureNames, deviceConfig.features);
    354     }
    355 
    356     @Test
    357     public void testDeviceConfig() {
    358         byte[] dump = executeShellCommand("cmd activity get-config --proto --device");
    359         GlobalConfigurationProto globalConfig;
    360         try {
    361             globalConfig = GlobalConfigurationProto.parseFrom(dump);
    362         } catch (InvalidProtocolBufferNanoException ex) {
    363             throw new RuntimeException("Failed to parse get-config:\n"
    364                     + new String(dump, StandardCharsets.UTF_8), ex);
    365         }
    366 
    367         Configuration config = mContext.getResources().getConfiguration();
    368         DisplayManager dm = mContext.getSystemService(DisplayManager.class);
    369         Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
    370         DisplayMetrics metrics = new DisplayMetrics();
    371         display.getMetrics(metrics);
    372 
    373         checkResourceConfig(config, metrics, globalConfig.resources);
    374         checkDeviceConfig(dm, globalConfig.device);
    375     }
    376 }
    377