1 /*
2  * Copyright (C) 2022 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;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.IBinder;
22 
23 import com.android.internal.util.Preconditions;
24 
25 import java.util.List;
26 
27 /**
28  * Privileges granted to a Process that allows it to execute starts from the background.
29  * @hide
30  */
31 public class BackgroundStartPrivileges {
32     /** No privileges. */
33     public static final BackgroundStartPrivileges NONE = new BackgroundStartPrivileges(
34             false, false, null);
35     /** Allow activity starts (and implies allowing foreground service starts).  */
36     public static final BackgroundStartPrivileges ALLOW_BAL = new BackgroundStartPrivileges(
37             true, true, null);
38     /** Allow foreground service starts. */
39     public static final BackgroundStartPrivileges ALLOW_FGS = new BackgroundStartPrivileges(
40             false, true, null);
41 
42     private final boolean mAllowsBackgroundActivityStarts;
43     private final boolean mAllowsBackgroundForegroundServiceStarts;
44     private final IBinder mOriginatingToken;
45 
BackgroundStartPrivileges(boolean allowsBackgroundActivityStarts, boolean allowsBackgroundForegroundServiceStarts, @Nullable IBinder originatingToken)46     private BackgroundStartPrivileges(boolean allowsBackgroundActivityStarts,
47             boolean allowsBackgroundForegroundServiceStarts, @Nullable IBinder originatingToken) {
48         Preconditions.checkArgument(
49                 !allowsBackgroundActivityStarts || allowsBackgroundForegroundServiceStarts,
50                 "backgroundActivityStarts implies bgFgServiceStarts");
51         mAllowsBackgroundActivityStarts = allowsBackgroundActivityStarts;
52         mAllowsBackgroundForegroundServiceStarts = allowsBackgroundForegroundServiceStarts;
53         mOriginatingToken = originatingToken;
54     }
55 
56     /**
57      * Return a token that allows background activity starts and attributes it to a specific
58      * originatingToken.
59      */
allowBackgroundActivityStarts( @ullable IBinder originatingToken)60     public static BackgroundStartPrivileges allowBackgroundActivityStarts(
61             @Nullable IBinder originatingToken) {
62         if (originatingToken == null) {
63             // try to avoid creating new instances
64             return ALLOW_BAL;
65         }
66         return new BackgroundStartPrivileges(true, true, originatingToken);
67     }
68 
69     /**
70      * Merge this {@link BackgroundStartPrivileges} with another {@link BackgroundStartPrivileges}.
71      *
72      * The resulting object will grant the union of the privileges of the merged objects.
73      * The originating tokens is retained only if both {@link BackgroundStartPrivileges} are the
74      * same.
75      *
76      * If one of the merged objects is {@link #NONE} then the other object is returned and the
77      * originating token is NOT cleared.
78      */
merge(@ullable BackgroundStartPrivileges other)79     public @NonNull BackgroundStartPrivileges merge(@Nullable BackgroundStartPrivileges other) {
80         // shortcuts in case
81         if (other == NONE || other == null) {
82             return this;
83         }
84         if (this == NONE) {
85             return other;
86         }
87 
88         boolean allowsBackgroundActivityStarts =
89                 this.allowsBackgroundActivityStarts() || other.allowsBackgroundActivityStarts();
90         boolean allowsBackgroundFgsStarts =
91                 this.allowsBackgroundFgsStarts() || other.allowsBackgroundFgsStarts();
92         if (this.mOriginatingToken == other.mOriginatingToken) {
93             // can reuse this?
94             if (this.mAllowsBackgroundActivityStarts == allowsBackgroundActivityStarts
95                     && this.mAllowsBackgroundForegroundServiceStarts == allowsBackgroundFgsStarts) {
96                 return this;
97             }
98             // can reuse other?
99             if (other.mAllowsBackgroundActivityStarts == allowsBackgroundActivityStarts
100                    && other.mAllowsBackgroundForegroundServiceStarts == allowsBackgroundFgsStarts) {
101                 return other;
102             }
103             // need to create a new instance (this should never happen)
104             return new BackgroundStartPrivileges(allowsBackgroundActivityStarts,
105                     allowsBackgroundFgsStarts, this.mOriginatingToken);
106         } else {
107             // no originating token -> can use standard instance
108             if (allowsBackgroundActivityStarts) {
109                 return ALLOW_BAL;
110             } else if (allowsBackgroundFgsStarts) {
111                 return ALLOW_FGS;
112             } else {
113                 return NONE;
114             }
115         }
116     }
117 
118     /**
119      * Merge a collection of {@link BackgroundStartPrivileges} into a single token.
120      *
121      * The resulting object will grant the union of the privileges of the merged objects.
122      * The originating tokens is retained only if all {@link BackgroundStartPrivileges} are the
123      * same.
124      *
125      * If the list contains {@link #NONE}s these are ignored.
126      */
merge( @ullable List<BackgroundStartPrivileges> list)127     public static @NonNull BackgroundStartPrivileges merge(
128             @Nullable List<BackgroundStartPrivileges> list) {
129         if (list == null || list.isEmpty()) {
130             return NONE;
131         }
132         BackgroundStartPrivileges current = list.get(0);
133         for (int i = list.size(); i-- > 1; ) {
134             current = current.merge(list.get(i));
135         }
136         return current;
137     }
138 
139     /**
140      * @return {@code true} if this grants the permission to start background activities from the
141      * background.
142      */
allowsBackgroundActivityStarts()143     public boolean allowsBackgroundActivityStarts() {
144         return mAllowsBackgroundActivityStarts;
145     }
146 
147     /**
148      * @return {@code true} this grants the permission to start foreground services from the
149      * background. */
allowsBackgroundFgsStarts()150     public boolean allowsBackgroundFgsStarts() {
151         return mAllowsBackgroundForegroundServiceStarts;
152     }
153 
154     /** @return true if this grants any privileges. */
allowsAny()155     public boolean allowsAny() {
156         return mAllowsBackgroundActivityStarts || mAllowsBackgroundForegroundServiceStarts;
157     }
158 
159     /** Return true if this grants no privileges. */
allowsNothing()160     public boolean allowsNothing() {
161         return !allowsAny();
162     }
163 
164     /**
165      * Gets the originating token.
166      *
167      * The originating token is optional information that allows to trace back the origin of this
168      * object. Besides debugging, this is used to e.g. identify privileges created by the
169      * notification service.
170      */
getOriginatingToken()171     public @Nullable IBinder getOriginatingToken() {
172         return mOriginatingToken;
173     }
174 
175     @Override
toString()176     public String toString() {
177         if (this == ALLOW_BAL) {
178             return "BSP.ALLOW_BAL";
179         }
180         if (this == ALLOW_FGS) {
181             return "BSP.ALLOW_FGS";
182         }
183         if (this == NONE) {
184             return "BSP.NONE";
185         }
186         return "BackgroundStartPrivileges["
187                 + "allowsBackgroundActivityStarts=" + mAllowsBackgroundActivityStarts
188                 + ", allowsBackgroundForegroundServiceStarts="
189                 + mAllowsBackgroundForegroundServiceStarts
190                 + ", originatingToken=" + mOriginatingToken
191                 + ']';
192     }
193 }
194