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.internal.compat;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 
24 import java.lang.annotation.Retention;
25 import java.lang.annotation.RetentionPolicy;
26 
27 /**
28  * This class contains all the possible override allowed states.
29  */
30 public final class OverrideAllowedState implements Parcelable {
31     @IntDef({
32             ALLOWED,
33             DISABLED_NOT_DEBUGGABLE,
34             DISABLED_NON_TARGET_SDK,
35             DISABLED_TARGET_SDK_TOO_HIGH,
36             DEFERRED_VERIFICATION,
37             LOGGING_ONLY_CHANGE,
38             PLATFORM_TOO_OLD
39     })
40     @Retention(RetentionPolicy.SOURCE)
41     public @interface State {
42     }
43 
44     /**
45      * Change can be overridden.
46      */
47     public static final int ALLOWED = 0;
48     /**
49      * Change cannot be overridden, due to the app not being debuggable.
50      */
51     public static final int DISABLED_NOT_DEBUGGABLE = 1;
52     /**
53      * Change cannot be overridden, due to the build being non-debuggable and the change being
54      * enabled regardless of targetSdk.
55      */
56     public static final int DISABLED_NON_TARGET_SDK = 2;
57     /**
58      * Change cannot be overridden, due to the app's targetSdk being above the change's targetSdk.
59      */
60     public static final int DISABLED_TARGET_SDK_TOO_HIGH = 3;
61      /**
62      * Change override decision is currently being deferred, due to the app not being installed yet.
63      */
64     public static final int DEFERRED_VERIFICATION = 4;
65     /**
66      * Change is marked as logging only, and cannot be toggled.
67      */
68     public static final int LOGGING_ONLY_CHANGE = 5;
69     /**
70      * Change is gated by a target sdk version newer than the current platform sdk version.
71      */
72     public static final int PLATFORM_TOO_OLD = 6;
73 
74     @State
75     public final int state;
76     public final int appTargetSdk;
77     public final int changeIdTargetSdk;
78 
OverrideAllowedState(Parcel parcel)79     private OverrideAllowedState(Parcel parcel) {
80         state = parcel.readInt();
81         appTargetSdk = parcel.readInt();
82         changeIdTargetSdk = parcel.readInt();
83     }
84 
OverrideAllowedState(@tate int state, int appTargetSdk, int changeIdTargetSdk)85     public OverrideAllowedState(@State int state, int appTargetSdk, int changeIdTargetSdk) {
86         this.state = state;
87         this.appTargetSdk = appTargetSdk;
88         this.changeIdTargetSdk = changeIdTargetSdk;
89     }
90 
91     @Override
describeContents()92     public int describeContents() {
93         return 0;
94     }
95 
96     @Override
writeToParcel(Parcel out, int flags)97     public void writeToParcel(Parcel out, int flags) {
98         out.writeInt(state);
99         out.writeInt(appTargetSdk);
100         out.writeInt(changeIdTargetSdk);
101     }
102 
103     /**
104      * Enforces the policy for overriding compat changes.
105      *
106      * @param changeId    the change id that was attempted to be overridden.
107      * @param packageName the package for which the attempt was made.
108      * @throws SecurityException if the policy forbids this operation.
109      */
enforce(long changeId, String packageName)110     public void enforce(long changeId, String packageName)
111             throws SecurityException {
112         switch (state) {
113             case ALLOWED:
114             case DEFERRED_VERIFICATION:
115                 return;
116             case DISABLED_NOT_DEBUGGABLE:
117                 throw new SecurityException(
118                         "Cannot override a change on a non-debuggable app and user build.");
119             case DISABLED_NON_TARGET_SDK:
120                 throw new SecurityException(
121                         "Cannot override a default enabled/disabled change on a user build.");
122             case DISABLED_TARGET_SDK_TOO_HIGH:
123                 throw new SecurityException(String.format(
124                         "Cannot override %1$d for %2$s because the app's targetSdk (%3$d) is "
125                                 + "above the change's targetSdk threshold (%4$d)",
126                         changeId, packageName, appTargetSdk, changeIdTargetSdk));
127             case LOGGING_ONLY_CHANGE:
128                 throw new SecurityException(String.format(
129                         "Cannot override %1$d because it is marked as a logging-only change.",
130                         changeId));
131             case PLATFORM_TOO_OLD:
132                 throw new SecurityException(String.format(
133                         "Cannot override %1$d for %2$s because the change's targetSdk threshold "
134                                 + "(%3$d) is above the platform sdk.",
135                         changeId, packageName, changeIdTargetSdk));
136         }
137     }
138 
139     public static final @NonNull
140             Parcelable.Creator<OverrideAllowedState> CREATOR =
141                 new Parcelable.Creator<OverrideAllowedState>() {
142                 public OverrideAllowedState createFromParcel(Parcel parcel) {
143                     OverrideAllowedState info = new OverrideAllowedState(parcel);
144                     return info;
145                 }
146 
147                 public OverrideAllowedState[] newArray(int size) {
148                     return new OverrideAllowedState[size];
149                 }
150             };
151 
152     @Override
equals(Object obj)153     public boolean equals(Object obj) {
154         if (this == obj) {
155             return true;
156         }
157         if (obj == null) {
158             return false;
159         }
160         if (!(obj instanceof OverrideAllowedState)) {
161             return false;
162         }
163         OverrideAllowedState otherState = (OverrideAllowedState) obj;
164         return state == otherState.state
165                 && appTargetSdk == otherState.appTargetSdk
166                 && changeIdTargetSdk == otherState.changeIdTargetSdk;
167     }
168 
stateName()169     private String stateName() {
170         switch (state) {
171             case ALLOWED:
172                 return "ALLOWED";
173             case DISABLED_NOT_DEBUGGABLE:
174                 return "DISABLED_NOT_DEBUGGABLE";
175             case DISABLED_NON_TARGET_SDK:
176                 return "DISABLED_NON_TARGET_SDK";
177             case DISABLED_TARGET_SDK_TOO_HIGH:
178                 return "DISABLED_TARGET_SDK_TOO_HIGH";
179             case DEFERRED_VERIFICATION:
180                 return "DEFERRED_VERIFICATION";
181             case LOGGING_ONLY_CHANGE:
182                 return "LOGGING_ONLY_CHANGE";
183             case PLATFORM_TOO_OLD:
184                 return "PLATFORM_TOO_OLD";
185         }
186         return "UNKNOWN";
187     }
188 
189     @Override
toString()190     public String toString() {
191         return "OverrideAllowedState(state=" + stateName() + "; appTargetSdk=" + appTargetSdk
192                 + "; changeIdTargetSdk=" + changeIdTargetSdk + ")";
193     }
194 }
195