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