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