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 android.content.om; 18 19 import static android.annotation.SystemApi.Client.SYSTEM_SERVER; 20 21 import static com.android.internal.util.Preconditions.checkNotNull; 22 23 import android.annotation.IntDef; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.SuppressLint; 27 import android.annotation.SystemApi; 28 import android.os.Bundle; 29 import android.os.Parcel; 30 import android.os.Parcelable; 31 import android.os.UserHandle; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.util.ArrayList; 36 import java.util.Iterator; 37 import java.util.List; 38 import java.util.Locale; 39 import java.util.Objects; 40 41 /** 42 * A container for a batch of requests to the OverlayManager. 43 * 44 * <p>An app can get an {@link OverlayManagerTransaction} with the specified {@link OverlayManager} 45 * to handle the transaction. The app can register multiple overlays and unregister multiple 46 * registered overlays in one transaction commitment. 47 * 48 * <p>The below example is registering a {@code updatingOverlay} and unregistering a {@code 49 * deprecatedOverlay} in one transaction commitment. 50 * 51 * <pre>{@code 52 * final OverlayManager overlayManager = ctx.getSystemService(OverlayManager.class); 53 * final OverlayManagerTransaction transaction = new OverlayManagerTransaction(overlayManager); 54 * transaction.registerFabricatedOverlay(updatingOverlay); 55 * transaction.unregisterFabricatedOverlay(deprecatedOverlay) 56 * transaction.commit(); 57 * }</pre> 58 * 59 * @see OverlayManager 60 * @see FabricatedOverlay 61 */ 62 public final class OverlayManagerTransaction implements Parcelable { 63 // TODO: remove @hide from this class when OverlayManager is added to the 64 // SDK, but keep OverlayManagerTransaction.Request @hidden 65 private final List<Request> mRequests; 66 private final boolean mSelfTargeting; 67 68 /** 69 * Container for a batch of requests to the OverlayManagerService. 70 * 71 * <p>Transactions are created using a builder interface. Example usage: 72 * <pre>{@code 73 * final OverlayManager om = ctx.getSystemService(OverlayManager.class); 74 * final OverlayManagerTransaction t = new OverlayManagerTransaction.Builder() 75 * .setEnabled(...) 76 * .setEnabled(...) 77 * .build(); 78 * om.commit(t); 79 * }</pre> 80 */ OverlayManagerTransaction( @onNull final List<Request> requests, boolean selfTargeting)81 private OverlayManagerTransaction( 82 @NonNull final List<Request> requests, boolean selfTargeting) { 83 Objects.requireNonNull(requests); 84 if (requests.contains(null)) { 85 throw new IllegalArgumentException("null request"); 86 } 87 mRequests = requests; 88 mSelfTargeting = selfTargeting; 89 } 90 91 /** 92 * Get an overlay manager transaction. 93 * 94 * @return a new {@link OverlayManagerTransaction} instance. 95 */ 96 @NonNull newInstance()97 public static OverlayManagerTransaction newInstance() { 98 return new OverlayManagerTransaction(new ArrayList<>(), true /* selfTargeting */); 99 } 100 OverlayManagerTransaction(@onNull final Parcel source)101 private OverlayManagerTransaction(@NonNull final Parcel source) { 102 final int size = source.readInt(); 103 mRequests = new ArrayList<>(size); 104 for (int i = 0; i < size; i++) { 105 final int request = source.readInt(); 106 final OverlayIdentifier overlay = source.readParcelable(null, android.content.om.OverlayIdentifier.class); 107 final int userId = source.readInt(); 108 final Bundle extras = source.readBundle(null); 109 mRequests.add(new Request(request, overlay, userId, extras)); 110 } 111 mSelfTargeting = false; 112 } 113 114 /** 115 * Get the iterator of requests 116 * 117 * @return the iterator of request 118 * @hide 119 */ 120 @SuppressLint("ReferencesHidden") 121 @NonNull 122 @SystemApi(client = SYSTEM_SERVER) getRequests()123 public Iterator<Request> getRequests() { 124 return mRequests.iterator(); 125 } 126 127 /** 128 * {@inheritDoc} 129 * 130 * @hide 131 */ 132 @Override toString()133 public String toString() { 134 return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests); 135 } 136 137 /** 138 * A single unit of the transaction, such as a request to enable an 139 * overlay, or to disable an overlay. 140 * 141 * @hide 142 */ 143 @SystemApi(client = SYSTEM_SERVER) 144 public static final class Request { 145 @IntDef(prefix = "TYPE_", value = { 146 TYPE_SET_ENABLED, 147 TYPE_SET_DISABLED, 148 }) 149 @Retention(RetentionPolicy.SOURCE) 150 @interface RequestType {} 151 152 public static final int TYPE_SET_ENABLED = 0; 153 public static final int TYPE_SET_DISABLED = 1; 154 public static final int TYPE_REGISTER_FABRICATED = 2; 155 public static final int TYPE_UNREGISTER_FABRICATED = 3; 156 157 public static final String BUNDLE_FABRICATED_OVERLAY = "fabricated_overlay"; 158 159 @RequestType 160 public final int type; 161 @NonNull 162 public final OverlayIdentifier overlay; 163 public final int userId; 164 165 @SuppressLint("NullableCollection") 166 @Nullable 167 public final Bundle extras; 168 Request(@equestType final int type, @NonNull final OverlayIdentifier overlay, final int userId)169 public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, 170 final int userId) { 171 this(type, overlay, userId, null /* extras */); 172 } 173 Request(@equestType final int type, @NonNull final OverlayIdentifier overlay, final int userId, @Nullable Bundle extras)174 public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, 175 final int userId, @Nullable Bundle extras) { 176 this.type = type; 177 this.overlay = overlay; 178 this.userId = userId; 179 this.extras = extras; 180 } 181 182 @Override toString()183 public String toString() { 184 return String.format(Locale.US, "Request{type=0x%02x (%s), overlay=%s, userId=%d}", 185 type, typeToString(), overlay, userId); 186 } 187 188 /** 189 * Translate the request type into a human readable string. Only 190 * intended for debugging. 191 * 192 * @hide 193 */ typeToString()194 public String typeToString() { 195 switch (type) { 196 case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED"; 197 case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED"; 198 case TYPE_REGISTER_FABRICATED: return "TYPE_REGISTER_FABRICATED"; 199 case TYPE_UNREGISTER_FABRICATED: return "TYPE_UNREGISTER_FABRICATED"; 200 default: return String.format("TYPE_UNKNOWN (0x%02x)", type); 201 } 202 } 203 } 204 205 /** 206 * Builder class for OverlayManagerTransaction objects. 207 * TODO(b/269197647): mark the API used by the systemUI. 208 * @hide 209 */ 210 public static final class Builder { 211 private final List<Request> mRequests = new ArrayList<>(); 212 213 /** 214 * Request that an overlay package be enabled and change its loading 215 * order to the last package to be loaded, or disabled 216 * 217 * If the caller has the correct permissions, it is always possible to 218 * disable an overlay. Due to technical and security reasons it may not 219 * always be possible to enable an overlay, for instance if the overlay 220 * does not successfully overlay any target resources due to 221 * overlayable policy restrictions. 222 * 223 * An enabled overlay is a part of target package's resources, i.e. it will 224 * be part of any lookups performed via {@link android.content.res.Resources} 225 * and {@link android.content.res.AssetManager}. A disabled overlay will no 226 * longer affect the resources of the target package. If the target is 227 * currently running, its outdated resources will be replaced by new ones. 228 * 229 * @param overlay The name of the overlay package. 230 * @param enable true to enable the overlay, false to disable it. 231 * @return this Builder object, so you can chain additional requests 232 */ setEnabled(@onNull OverlayIdentifier overlay, boolean enable)233 public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable) { 234 return setEnabled(overlay, enable, UserHandle.myUserId()); 235 } 236 237 /** 238 * @hide 239 */ setEnabled(@onNull OverlayIdentifier overlay, boolean enable, int userId)240 public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, int userId) { 241 checkNotNull(overlay); 242 @Request.RequestType final int type = 243 enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED; 244 mRequests.add(new Request(type, overlay, userId)); 245 return this; 246 } 247 248 /** 249 * Registers the fabricated overlay with the overlay manager so it can be enabled and 250 * disabled for any user. 251 * 252 * The fabricated overlay is initialized in a disabled state. If an overlay is re-registered 253 * the existing overlay will be replaced by the newly registered overlay and the enabled 254 * state of the overlay will be left unchanged if the target package and target overlayable 255 * have not changed. 256 * 257 * @param overlay the overlay to register with the overlay manager 258 * 259 * @hide 260 */ 261 @NonNull registerFabricatedOverlay(@onNull FabricatedOverlay overlay)262 public Builder registerFabricatedOverlay(@NonNull FabricatedOverlay overlay) { 263 mRequests.add(generateRegisterFabricatedOverlayRequest(overlay)); 264 return this; 265 } 266 267 /** 268 * Disables and removes the overlay from the overlay manager for all users. 269 * 270 * @param overlay the overlay to disable and remove 271 * 272 * @hide 273 */ 274 @NonNull unregisterFabricatedOverlay(@onNull OverlayIdentifier overlay)275 public Builder unregisterFabricatedOverlay(@NonNull OverlayIdentifier overlay) { 276 mRequests.add(generateUnRegisterFabricatedOverlayRequest(overlay)); 277 return this; 278 } 279 280 /** 281 * Create a new transaction out of the requests added so far. Execute 282 * the transaction by calling OverlayManager#commit. 283 * 284 * @see OverlayManager#commit 285 * @return a new transaction 286 */ 287 @NonNull build()288 public OverlayManagerTransaction build() { 289 return new OverlayManagerTransaction(mRequests, false /* selfTargeting */); 290 } 291 } 292 293 /** 294 * {@inheritDoc} 295 */ 296 @Override describeContents()297 public int describeContents() { 298 return 0; 299 } 300 301 /** 302 * {@inheritDoc} 303 */ 304 @Override writeToParcel(@onNull Parcel dest, int flags)305 public void writeToParcel(@NonNull Parcel dest, int flags) { 306 final int size = mRequests.size(); 307 dest.writeInt(size); 308 for (int i = 0; i < size; i++) { 309 final Request req = mRequests.get(i); 310 dest.writeInt(req.type); 311 dest.writeParcelable(req.overlay, flags); 312 dest.writeInt(req.userId); 313 dest.writeBundle(req.extras); 314 } 315 } 316 317 @NonNull 318 public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR = 319 new Parcelable.Creator<OverlayManagerTransaction>() { 320 321 @Override 322 public OverlayManagerTransaction createFromParcel(Parcel source) { 323 return new OverlayManagerTransaction(source); 324 } 325 326 @Override 327 public OverlayManagerTransaction[] newArray(int size) { 328 return new OverlayManagerTransaction[size]; 329 } 330 }; 331 generateRegisterFabricatedOverlayRequest( @onNull FabricatedOverlay overlay)332 private static Request generateRegisterFabricatedOverlayRequest( 333 @NonNull FabricatedOverlay overlay) { 334 Objects.requireNonNull(overlay); 335 336 final Bundle extras = new Bundle(); 337 extras.putParcelable(Request.BUNDLE_FABRICATED_OVERLAY, overlay.mOverlay); 338 return new Request(Request.TYPE_REGISTER_FABRICATED, overlay.getIdentifier(), 339 UserHandle.USER_ALL, extras); 340 } 341 generateUnRegisterFabricatedOverlayRequest( @onNull OverlayIdentifier overlayIdentifier)342 private static Request generateUnRegisterFabricatedOverlayRequest( 343 @NonNull OverlayIdentifier overlayIdentifier) { 344 Objects.requireNonNull(overlayIdentifier); 345 346 return new Request(Request.TYPE_UNREGISTER_FABRICATED, overlayIdentifier, 347 UserHandle.USER_ALL); 348 } 349 350 /** 351 * Registers the fabricated overlays with the overlay manager so it can be used to overlay 352 * the app resources in runtime. 353 * 354 * <p>If an overlay is re-registered the existing overlay will be replaced by the newly 355 * registered overlay. The registered overlay will be left unchanged until the target 356 * package or target overlayable is changed. 357 * 358 * @param overlay the overlay to register with the overlay manager 359 */ 360 @NonNull registerFabricatedOverlay(@onNull FabricatedOverlay overlay)361 public void registerFabricatedOverlay(@NonNull FabricatedOverlay overlay) { 362 mRequests.add(generateRegisterFabricatedOverlayRequest(overlay)); 363 } 364 365 /** 366 * Unregisters the registered overlays from the overlay manager. 367 * 368 * @param overlay the overlay to be unregistered 369 * 370 * @see OverlayManager#getOverlayInfosForTarget(String) 371 * @see OverlayInfo#getOverlayIdentifier() 372 */ 373 @NonNull unregisterFabricatedOverlay(@onNull OverlayIdentifier overlay)374 public void unregisterFabricatedOverlay(@NonNull OverlayIdentifier overlay) { 375 mRequests.add(generateUnRegisterFabricatedOverlayRequest(overlay)); 376 } 377 378 /** 379 * Indicate whether the transaction is for self-targeting or not. 380 */ isSelfTargeting()381 boolean isSelfTargeting() { 382 return mSelfTargeting; 383 } 384 } 385