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 com.android.server.compat;
18 
19 import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD;
20 import static android.content.pm.PackageManager.MATCH_ANY_USER;
21 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
22 
23 import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
24 import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION;
25 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
26 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
27 import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
28 import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
29 import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
30 
31 import android.annotation.NonNull;
32 import android.content.Context;
33 import android.content.pm.ApplicationInfo;
34 import android.content.pm.PackageManager;
35 import android.content.pm.PackageManager.NameNotFoundException;
36 import android.database.ContentObserver;
37 import android.os.Handler;
38 import android.provider.Settings;
39 
40 import com.android.internal.annotations.VisibleForTesting;
41 import com.android.internal.compat.AndroidBuildClassifier;
42 import com.android.internal.compat.IOverrideValidator;
43 import com.android.internal.compat.OverrideAllowedState;
44 
45 /**
46  * Implementation of the policy for allowing compat change overrides.
47  */
48 public class OverrideValidatorImpl extends IOverrideValidator.Stub {
49 
50     private AndroidBuildClassifier mAndroidBuildClassifier;
51     private Context mContext;
52     private CompatConfig mCompatConfig;
53     private boolean mForceNonDebuggableFinalBuild;
54 
55     private class SettingsObserver extends ContentObserver {
SettingsObserver()56         SettingsObserver() {
57             super(new Handler());
58         }
59         @Override
onChange(boolean selfChange)60         public void onChange(boolean selfChange) {
61             mForceNonDebuggableFinalBuild = Settings.Global.getInt(
62                 mContext.getContentResolver(),
63                 Settings.Global.FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT,
64                 0) == 1;
65         }
66     }
67 
68     @VisibleForTesting
OverrideValidatorImpl(AndroidBuildClassifier androidBuildClassifier, Context context, CompatConfig config)69     OverrideValidatorImpl(AndroidBuildClassifier androidBuildClassifier,
70                           Context context, CompatConfig config) {
71         mAndroidBuildClassifier = androidBuildClassifier;
72         mContext = context;
73         mCompatConfig = config;
74         mForceNonDebuggableFinalBuild = false;
75     }
76 
77     /**
78      * Check the allowed state for the given changeId and packageName on a recheck.
79      *
80      * <p>Recheck happens when the given app is getting updated. In this case we cannot do a
81      * permission check on the caller, so we're using the fact that the override was present as
82      * proof that the original caller was allowed to set this override.
83      */
getOverrideAllowedStateForRecheck(long changeId, @NonNull String packageName)84     OverrideAllowedState getOverrideAllowedStateForRecheck(long changeId,
85             @NonNull String packageName) {
86         return getOverrideAllowedStateInternal(changeId, packageName, true);
87     }
88 
89     @Override
getOverrideAllowedState(long changeId, String packageName)90     public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) {
91         return getOverrideAllowedStateInternal(changeId, packageName, false);
92     }
93 
getOverrideAllowedStateInternal(long changeId, String packageName, boolean isRecheck)94     private OverrideAllowedState getOverrideAllowedStateInternal(long changeId, String packageName,
95             boolean isRecheck) {
96         if (mCompatConfig.isLoggingOnly(changeId)) {
97             return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1);
98         }
99 
100         boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild()
101                                     && !mForceNonDebuggableFinalBuild;
102         boolean finalBuild = mAndroidBuildClassifier.isFinalBuild()
103                                 || mForceNonDebuggableFinalBuild;
104         int maxTargetSdk = mCompatConfig.maxTargetSdkForChangeIdOptIn(changeId);
105         boolean disabled = mCompatConfig.isDisabled(changeId);
106 
107         // Allow any override for userdebug or eng builds.
108         if (debuggableBuild) {
109             return new OverrideAllowedState(ALLOWED, -1, -1);
110         }
111         if (maxTargetSdk >= mAndroidBuildClassifier.platformTargetSdk()) {
112             return new OverrideAllowedState(PLATFORM_TOO_OLD, -1, maxTargetSdk);
113         }
114         PackageManager packageManager = mContext.getPackageManager();
115         if (packageManager == null) {
116             throw new IllegalStateException("No PackageManager!");
117         }
118         ApplicationInfo applicationInfo;
119         try {
120             applicationInfo = packageManager.getApplicationInfo(packageName, MATCH_ANY_USER);
121         } catch (NameNotFoundException e) {
122             return new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1);
123         }
124         // If the change is annotated as @Overridable, apps with the specific permission can
125         // set the override even on production builds. When rechecking the override, e.g. during an
126         // app update we can bypass this check, as it wouldn't have been here in the first place.
127         if (mCompatConfig.isOverridable(changeId)
128                 && (isRecheck
129                         || mContext.checkCallingOrSelfPermission(
130                                 OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
131                                         == PERMISSION_GRANTED)) {
132             return new OverrideAllowedState(ALLOWED, -1, -1);
133         }
134         int appTargetSdk = applicationInfo.targetSdkVersion;
135         // Only allow overriding debuggable apps.
136         if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
137             return new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1);
138         }
139         // Allow overriding any change for debuggable apps on non-final builds.
140         if (!finalBuild) {
141             return new OverrideAllowedState(ALLOWED, appTargetSdk, maxTargetSdk);
142         }
143         // Do not allow overriding default enabled changes on user builds
144         if (maxTargetSdk == -1 && !disabled) {
145             return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, maxTargetSdk);
146         }
147         // Only allow to opt-in for a targetSdk gated change.
148         if (disabled || appTargetSdk <= maxTargetSdk) {
149             return new OverrideAllowedState(ALLOWED, appTargetSdk, maxTargetSdk);
150         }
151         return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, maxTargetSdk);
152     }
153 
registerContentObserver()154     void registerContentObserver() {
155         mContext.getContentResolver().registerContentObserver(
156                 Settings.Global.getUriFor(
157                     Settings.Global.FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT),
158                 false,
159                 new SettingsObserver());
160     }
161 
forceNonDebuggableFinalForTest(boolean value)162     void forceNonDebuggableFinalForTest(boolean value) {
163         mForceNonDebuggableFinalBuild = value;
164     }
165 }
166