Merge "Add CompatChanges API." am: 54036c0525 am: 9f9657ee5e am: f1f1b1402a

Change-Id: Id473cf123c184eb4247f93ebc9d96bbb87f9f7a7
diff --git a/api/system-current.txt b/api/system-current.txt
index 7903fba..d7c9a77 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1152,6 +1152,16 @@
 
 }
 
+package android.app.compat {
+
+  public final class CompatChanges {
+    method public static boolean isChangeEnabled(long);
+    method public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle);
+    method public static boolean isChangeEnabled(long, int);
+  }
+
+}
+
 package android.app.contentsuggestions {
 
   public final class ClassificationsRequest implements android.os.Parcelable {
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
new file mode 100644
index 0000000..5b36789
--- /dev/null
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.compat;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.compat.Compatibility;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+
+import com.android.internal.compat.IPlatformCompat;
+
+/**
+ * CompatChanges APIs - to be used by platform code only (including mainline
+ * modules).
+ *
+ * @hide
+ */
+@SystemApi
+public final class CompatChanges {
+    private CompatChanges() {}
+
+    /**
+     * Query if a given compatibility change is enabled for the current process. This method is
+     * intended to be called by code running inside a process of the affected app only.
+     *
+     * <p>If this method returns {@code true}, the calling code should implement the compatibility
+     * change, resulting in differing behaviour compared to earlier releases. If this method returns
+     * {@code false}, the calling code should behave as it did in earlier releases.
+     *
+     * @param changeId The ID of the compatibility change in question.
+     * @return {@code true} if the change is enabled for the current app.
+     */
+    public static boolean isChangeEnabled(long changeId) {
+        return Compatibility.isChangeEnabled(changeId);
+    }
+
+    /**
+     * Same as {@code #isChangeEnabled(long)}, except this version should be called on behalf of an
+     * app from a different process that's performing work for the app.
+     *
+     * <p> Note that this involves a binder call to the system server (unless running in the system
+     * server). If the binder call fails, a {@code RuntimeException} will be thrown.
+     *
+     * <p> Caller must have android.permission.READ_COMPAT_CHANGE_CONFIG permission. If it
+     * doesn't, a {@code RuntimeException} will be thrown.
+     *
+     * @param changeId    The ID of the compatibility change in question.
+     * @param packageName The package name of the app in question.
+     * @param user        The user that the operation is done for.
+     * @return {@code true} if the change is enabled for the current app.
+     */
+    public static boolean isChangeEnabled(long changeId, @NonNull String packageName,
+            @NonNull UserHandle user) {
+        IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        try {
+            return platformCompat.isChangeEnabledByPackageName(changeId, packageName,
+                    user.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Same as {@code #isChangeEnabled(long)}, except this version should be called on behalf of an
+     * app from a different process that's performing work for the app.
+     *
+     * <p> Note that this involves a binder call to the system server (unless running in the system
+     * server). If the binder call fails, {@code RuntimeException}  will be thrown.
+     *
+     * <p> Caller must have android.permission.READ_COMPAT_CHANGE_CONFIG permission. If it
+     * doesn't, a {@code RuntimeException} will be thrown.
+     *
+     * <p> Returns {@code true} if there are no installed packages for the required UID, or if the
+     * change is enabled for ALL of the installed packages associated with the provided UID. Please
+     * use a more specific API if you want a different behaviour for multi-package UIDs.
+     *
+     * @param changeId The ID of the compatibility change in question.
+     * @param uid      The UID of the app in question.
+     * @return {@code true} if the change is enabled for the current app.
+     */
+    public static boolean isChangeEnabled(long changeId, int uid) {
+        IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        try {
+            return platformCompat.isChangeEnabledByUid(changeId, uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 38454ec..9f15faf 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -38,6 +38,7 @@
         "mockito-target-minus-junit4",
         "ub-uiautomator",
         "platform-test-annotations",
+        "platform-compat-test-rules",
         "truth-prebuilt",
         "print-test-util-lib",
         "testng",
diff --git a/core/tests/coretests/src/android/app/compat/CompatChangesTest.java b/core/tests/coretests/src/android/app/compat/CompatChangesTest.java
new file mode 100644
index 0000000..fbd02ed
--- /dev/null
+++ b/core/tests/coretests/src/android/app/compat/CompatChangesTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.compat;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Instrumentation;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.os.Process;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+/**
+ * {@link CompatChanges} tests.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class CompatChangesTest {
+    static final long CHANGE_ID = 1L;
+
+    private Instrumentation mInstrumentation;
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+
+    private String getPackageName() {
+        return mInstrumentation.getTargetContext().getPackageName();
+    }
+
+    @Test
+    @EnableCompatChanges(CHANGE_ID)
+    public void testEnabledChange() {
+        assertThat(CompatChanges.isChangeEnabled(CHANGE_ID)).isTrue();
+        assertThat(CompatChanges.isChangeEnabled(CHANGE_ID, Process.myUid())).isTrue();
+        assertThat(CompatChanges.isChangeEnabled(CHANGE_ID, getPackageName(),
+                UserHandle.of(UserHandle.myUserId()))).isTrue();
+    }
+
+    @Test
+    @DisableCompatChanges(CHANGE_ID)
+    public void testDisabledChange() {
+        assertThat(CompatChanges.isChangeEnabled(CHANGE_ID)).isFalse();
+        assertThat(CompatChanges.isChangeEnabled(CHANGE_ID, Process.myUid())).isFalse();
+        assertThat(CompatChanges.isChangeEnabled(CHANGE_ID, getPackageName(),
+                UserHandle.of(UserHandle.myUserId()))).isFalse();
+    }
+}
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index c4e6427..af47430 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -21,6 +21,7 @@
 import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
@@ -107,7 +108,8 @@
     }
 
     @Override
-    public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
+    public boolean isChangeEnabledByPackageName(long changeId, String packageName,
+            @UserIdInt int userId) {
         checkCompatChangeReadAndLogPermission();
         ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
         if (appInfo == null) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompatNative.java b/services/core/java/com/android/server/compat/PlatformCompatNative.java
index 85dfbf4..5d7af65 100644
--- a/services/core/java/com/android/server/compat/PlatformCompatNative.java
+++ b/services/core/java/com/android/server/compat/PlatformCompatNative.java
@@ -16,6 +16,8 @@
 
 package com.android.server.compat;
 
+import android.annotation.UserIdInt;
+
 import com.android.internal.compat.IPlatformCompatNative;
 
 /**
@@ -39,7 +41,8 @@
     }
 
     @Override
-    public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
+    public boolean isChangeEnabledByPackageName(long changeId, String packageName,
+            @UserIdInt int userId) {
         return mPlatformCompat.isChangeEnabledByPackageName(changeId, packageName, userId);
     }