1 /* 2 * Copyright (C) 2020 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.app.compat; 18 19 import android.annotation.NonNull; 20 import android.annotation.RequiresPermission; 21 import android.annotation.SystemApi; 22 import android.compat.Compatibility; 23 import android.content.Context; 24 import android.os.RemoteException; 25 import android.os.ServiceManager; 26 import android.os.UserHandle; 27 28 import com.android.internal.compat.CompatibilityOverrideConfig; 29 import com.android.internal.compat.CompatibilityOverridesToRemoveConfig; 30 import com.android.internal.compat.IPlatformCompat; 31 32 import java.util.Map; 33 import java.util.Set; 34 35 /** 36 * CompatChanges APIs - to be used by platform code only (including mainline 37 * modules). 38 * 39 * @hide 40 */ 41 @SystemApi 42 public final class CompatChanges { 43 private static final ChangeIdStateCache QUERY_CACHE = new ChangeIdStateCache(); CompatChanges()44 private CompatChanges() {} 45 46 /** 47 * Query if a given compatibility change is enabled for the current process. This method is 48 * intended to be called by code running inside a process of the affected app only. 49 * 50 * <p>If this method returns {@code true}, the calling code should implement the compatibility 51 * change, resulting in differing behaviour compared to earlier releases. If this method returns 52 * {@code false}, the calling code should behave as it did in earlier releases. 53 * 54 * @param changeId The ID of the compatibility change in question. 55 * @return {@code true} if the change is enabled for the current app. 56 */ isChangeEnabled(long changeId)57 public static boolean isChangeEnabled(long changeId) { 58 return Compatibility.isChangeEnabled(changeId); 59 } 60 61 /** 62 * Same as {@code #isChangeEnabled(long)}, except this version should be called on behalf of an 63 * app from a different process that's performing work for the app. 64 * 65 * <p> Note that this involves a binder call to the system server (unless running in the system 66 * server). If the binder call fails, a {@code RuntimeException} will be thrown. 67 * 68 * @param changeId The ID of the compatibility change in question. 69 * @param packageName The package name of the app in question. 70 * @param user The user that the operation is done for. 71 * @return {@code true} if the change is enabled for the current app. 72 */ 73 @RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG, 74 android.Manifest.permission.LOG_COMPAT_CHANGE}) isChangeEnabled(long changeId, @NonNull String packageName, @NonNull UserHandle user)75 public static boolean isChangeEnabled(long changeId, @NonNull String packageName, 76 @NonNull UserHandle user) { 77 return QUERY_CACHE.query(ChangeIdStateQuery.byPackageName(changeId, packageName, 78 user.getIdentifier())); 79 } 80 81 /** 82 * Same as {@code #isChangeEnabled(long)}, except this version should be called on behalf of an 83 * app from a different process that's performing work for the app. 84 * 85 * <p> Note that this involves a binder call to the system server (unless running in the system 86 * server). If the binder call fails, {@code RuntimeException} will be thrown. 87 * 88 * <p> Returns {@code true} if there are no installed packages for the required UID, or if the 89 * change is enabled for ALL of the installed packages associated with the provided UID. Please 90 * use a more specific API if you want a different behaviour for multi-package UIDs. 91 * 92 * @param changeId The ID of the compatibility change in question. 93 * @param uid The UID of the app in question. 94 * @return {@code true} if the change is enabled for the current app. 95 */ 96 @RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG, 97 android.Manifest.permission.LOG_COMPAT_CHANGE}) isChangeEnabled(long changeId, int uid)98 public static boolean isChangeEnabled(long changeId, int uid) { 99 return QUERY_CACHE.query(ChangeIdStateQuery.byUid(changeId, uid)); 100 } 101 102 /** 103 * Associates app compat overrides with the given package and their respective change IDs. 104 * This will check whether the caller is allowed to perform this operation on the given apk and 105 * build. Only the installer package is allowed to set overrides on a non-debuggable final 106 * build and a non-test apk. 107 * 108 * <p>Note that calling this method doesn't remove previously added overrides for the given 109 * package if their change ID isn't in the given map, only replaces those that have the same 110 * change ID. 111 * 112 * @param packageName The package name of the app in question. 113 * @param overrides A map from change ID to the override applied for this change ID. 114 */ 115 @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) putPackageOverrides(@onNull String packageName, @NonNull Map<Long, PackageOverride> overrides)116 public static void putPackageOverrides(@NonNull String packageName, 117 @NonNull Map<Long, PackageOverride> overrides) { 118 IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( 119 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); 120 CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides); 121 try { 122 platformCompat.putOverridesOnReleaseBuilds(config, packageName); 123 } catch (RemoteException e) { 124 e.rethrowFromSystemServer(); 125 } 126 } 127 128 /** 129 * Removes app compat overrides for the given package. This will check whether the caller is 130 * allowed to perform this operation on the given apk and build. Only the installer package is 131 * allowed to clear overrides on a non-debuggable final build and a non-test apk. 132 * 133 * <p>Note that calling this method with an empty set is a no-op and no overrides will be 134 * removed for the given package. 135 * 136 * @param packageName The package name of the app in question. 137 * @param overridesToRemove A set of change IDs for which to remove overrides. 138 */ 139 @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) removePackageOverrides(@onNull String packageName, @NonNull Set<Long> overridesToRemove)140 public static void removePackageOverrides(@NonNull String packageName, 141 @NonNull Set<Long> overridesToRemove) { 142 IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( 143 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); 144 CompatibilityOverridesToRemoveConfig config = new CompatibilityOverridesToRemoveConfig( 145 overridesToRemove); 146 try { 147 platformCompat.removeOverridesOnReleaseBuilds(config, packageName); 148 } catch (RemoteException e) { 149 e.rethrowFromSystemServer(); 150 } 151 } 152 } 153