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