1 /* 2 * Copyright (C) 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 android.compat.testing; 18 19 import android.Manifest; 20 import android.app.Instrumentation; 21 import android.app.UiAutomation; 22 import android.compat.Compatibility; 23 import android.compat.Compatibility.ChangeConfig; 24 import android.content.Context; 25 import android.os.RemoteException; 26 import android.os.ServiceManager; 27 28 import androidx.test.platform.app.InstrumentationRegistry; 29 30 31 import com.android.internal.compat.CompatibilityChangeConfig; 32 import com.android.internal.compat.IPlatformCompat; 33 34 import libcore.junit.util.compat.CoreCompatChangeRule; 35 36 import org.junit.runners.model.Statement; 37 38 /** 39 * Allows tests to specify the which change to disable. 40 * 41 * <p>To use add the following to the test class. It will only change the behavior of a test method 42 * if it is annotated with 43 * {@link libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges} and/or 44 * {@link libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges}. 45 * </p> 46 * <pre> 47 * @Rule 48 * public TestRule compatChangeRule = new PlatformCompatChangeRule(); 49 * </pre> 50 * 51 * <p>Each test method that needs to disable a specific change needs to be annotated 52 * with {@link libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges} and/or 53 * {@link libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges} specifying the change 54 * id. e.g.: 55 * </p> 56 * <pre> 57 * @Test 58 * @DisableCompatChanges({42}) 59 * public void testAsIfChange42Disabled() { 60 * // check behavior 61 * } 62 * 63 * @Test 64 * @EnableCompatChanges({42}) 65 * public void testAsIfChange42Enabled() { 66 * // check behavior 67 * 68 * </pre> 69 */ 70 public class PlatformCompatChangeRule extends CoreCompatChangeRule { 71 72 @Override createStatementForConfig(final Statement statement, ChangeConfig config)73 protected Statement createStatementForConfig(final Statement statement, ChangeConfig config) { 74 return new CompatChangeStatement(statement, config); 75 } 76 77 78 private static class CompatChangeStatement extends Statement { 79 private final Statement mTestStatement; 80 private final ChangeConfig mConfig; 81 CompatChangeStatement(Statement testStatement, ChangeConfig config)82 private CompatChangeStatement(Statement testStatement, ChangeConfig config) { 83 this.mTestStatement = testStatement; 84 this.mConfig = config; 85 } 86 87 @Override evaluate()88 public void evaluate() throws Throwable { 89 Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 90 UiAutomation uiAutomation = instrumentation.getUiAutomation(); 91 String packageName = instrumentation.getTargetContext().getPackageName(); 92 IPlatformCompat platformCompat = IPlatformCompat.Stub 93 .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); 94 if (platformCompat == null) { 95 throw new IllegalStateException("Could not get IPlatformCompat service!"); 96 } 97 adoptShellPermissions(uiAutomation); 98 Compatibility.setOverrides(mConfig); 99 try { 100 platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig), 101 packageName); 102 try { 103 mTestStatement.evaluate(); 104 } finally { 105 adoptShellPermissions(uiAutomation); 106 platformCompat.clearOverridesForTest(packageName); 107 } 108 } catch (RemoteException e) { 109 throw new RuntimeException("Could not call IPlatformCompat binder method!", e); 110 } finally { 111 uiAutomation.dropShellPermissionIdentity(); 112 Compatibility.clearOverrides(); 113 } 114 } 115 adoptShellPermissions(UiAutomation uiAutomation)116 private static void adoptShellPermissions(UiAutomation uiAutomation) { 117 uiAutomation.adoptShellPermissionIdentity( 118 Manifest.permission.LOG_COMPAT_CHANGE, 119 Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG, 120 Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD, 121 Manifest.permission.READ_COMPAT_CHANGE_CONFIG, 122 Manifest.permission.INTERACT_ACROSS_USERS_FULL); 123 } 124 } 125 } 126