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 android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.SuppressLint; 25 import android.annotation.SystemApi; 26 import android.os.Bundle; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 import android.os.UserHandle; 30 import android.text.TextUtils; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Collections; 37 import java.util.Iterator; 38 import java.util.List; 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 OverlayConstraint[] constraints = source.createTypedArray(OverlayConstraint.CREATOR); 110 mRequests.add(new Request(request, overlay, userId, extras, 111 Arrays.asList(constraints))); 112 } 113 mSelfTargeting = false; 114 } 115 116 /** 117 * Get the iterator of requests 118 * 119 * @return the iterator of request 120 * 121 * @hide 122 */ 123 @SuppressLint("ReferencesHidden") 124 @NonNull 125 @SystemApi(client = SYSTEM_SERVER) getRequests()126 public Iterator<Request> getRequests() { 127 return mRequests.iterator(); 128 } 129 130 /** 131 * {@inheritDoc} 132 * 133 * @hide 134 */ 135 @Override toString()136 public String toString() { 137 return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests); 138 } 139 140 /** 141 * A single unit of the transaction, such as a request to enable an 142 * overlay, or to disable an overlay. 143 * 144 * @hide 145 */ 146 @SystemApi(client = SYSTEM_SERVER) 147 public static final class Request { 148 @IntDef(prefix = "TYPE_", value = { 149 TYPE_SET_ENABLED, 150 TYPE_SET_DISABLED, 151 TYPE_REGISTER_FABRICATED, 152 TYPE_UNREGISTER_FABRICATED, 153 }) 154 @Retention(RetentionPolicy.SOURCE) 155 @interface RequestType {} 156 157 public static final int TYPE_SET_ENABLED = 0; 158 public static final int TYPE_SET_DISABLED = 1; 159 public static final int TYPE_REGISTER_FABRICATED = 2; 160 public static final int TYPE_UNREGISTER_FABRICATED = 3; 161 162 public static final String BUNDLE_FABRICATED_OVERLAY = "fabricated_overlay"; 163 164 @RequestType 165 public final int type; 166 @NonNull 167 public final OverlayIdentifier overlay; 168 public final int userId; 169 170 @SuppressLint("NullableCollection") 171 @Nullable 172 public final Bundle extras; 173 174 /** 175 * @hide 176 */ 177 @NonNull 178 public final List<OverlayConstraint> constraints; 179 Request(@equestType final int type, @NonNull final OverlayIdentifier overlay, final int userId)180 public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, 181 final int userId) { 182 this(type, overlay, userId, null /* extras */, 183 Collections.emptyList() /* constraints */); 184 } 185 Request(@equestType final int type, @NonNull final OverlayIdentifier overlay, final int userId, @Nullable Bundle extras)186 public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, 187 final int userId, @Nullable Bundle extras) { 188 this(type, overlay, userId, extras, Collections.emptyList() /* constraints */); 189 } 190 191 /** 192 * @hide 193 */ Request(@equestType final int type, @NonNull final OverlayIdentifier overlay, final int userId, @NonNull List<OverlayConstraint> constraints)194 public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, 195 final int userId, @NonNull List<OverlayConstraint> constraints) { 196 this(type, overlay, userId, null /* extras */, constraints); 197 } 198 199 /** 200 * @hide 201 */ Request(@equestType final int type, @NonNull final OverlayIdentifier overlay, final int userId, @Nullable Bundle extras, @NonNull List<OverlayConstraint> constraints)202 public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, 203 final int userId, @Nullable Bundle extras, 204 @NonNull List<OverlayConstraint> constraints) { 205 this.type = type; 206 this.overlay = overlay; 207 this.userId = userId; 208 this.extras = extras; 209 Objects.requireNonNull(constraints); 210 this.constraints = constraints; 211 } 212 213 @Override toString()214 public String toString() { 215 return TextUtils.formatSimple( 216 "Request{type=0x%02x (%s), overlay=%s, userId=%d, constraints=%s}", 217 type, typeToString(), overlay, userId, 218 OverlayConstraint.constraintsToString(constraints)); 219 } 220 221 /** 222 * Translate the request type into a human readable string. Only 223 * intended for debugging. 224 * 225 * @hide 226 */ typeToString()227 public String typeToString() { 228 switch (type) { 229 case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED"; 230 case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED"; 231 case TYPE_REGISTER_FABRICATED: return "TYPE_REGISTER_FABRICATED"; 232 case TYPE_UNREGISTER_FABRICATED: return "TYPE_UNREGISTER_FABRICATED"; 233 default: return String.format("TYPE_UNKNOWN (0x%02x)", type); 234 } 235 } 236 } 237 238 /** 239 * Builder class for OverlayManagerTransaction objects. 240 * TODO(b/269197647): mark the API used by the systemUI. 241 * 242 * @hide 243 */ 244 public static final class Builder { 245 private final List<Request> mRequests = new ArrayList<>(); 246 private boolean mSelfTargeting = false; 247 248 /** 249 * Request that an overlay package be enabled and change its loading 250 * order to the last package to be loaded, or disabled 251 * 252 * If the caller has the correct permissions, it is always possible to 253 * disable an overlay. Due to technical and security reasons it may not 254 * always be possible to enable an overlay, for instance if the overlay 255 * does not successfully overlay any target resources due to 256 * overlayable policy restrictions. 257 * 258 * An enabled overlay is a part of target package's resources, i.e. it will 259 * be part of any lookups performed via {@link android.content.res.Resources} 260 * and {@link android.content.res.AssetManager}. A disabled overlay will no 261 * longer affect the resources of the target package. If the target is 262 * currently running, its outdated resources will be replaced by new ones. 263 * 264 * @param overlay The name of the overlay package. 265 * @param enable true to enable the overlay, false to disable it. 266 * @return this Builder object, so you can chain additional requests 267 */ setEnabled(@onNull OverlayIdentifier overlay, boolean enable)268 public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable) { 269 return setEnabled(overlay, enable, UserHandle.myUserId()); 270 } 271 272 /** 273 * @hide 274 */ setEnabled(@onNull OverlayIdentifier overlay, boolean enable, @NonNull List<OverlayConstraint> constraints)275 public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, 276 @NonNull List<OverlayConstraint> constraints) { 277 return setEnabled(overlay, enable, UserHandle.myUserId(), constraints); 278 } 279 280 /** 281 * @hide 282 */ setEnabled(@onNull OverlayIdentifier overlay, boolean enable, int userId)283 public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, int userId) { 284 return setEnabled(overlay, enable, userId, Collections.emptyList() /* constraints */); 285 } 286 287 /** 288 * @hide 289 */ setEnabled(@onNull OverlayIdentifier overlay, boolean enable, int userId, @NonNull List<OverlayConstraint> constraints)290 public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, int userId, 291 @NonNull List<OverlayConstraint> constraints) { 292 Objects.requireNonNull(overlay); 293 @Request.RequestType final int type = 294 enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED; 295 mRequests.add(new Request(type, overlay, userId, constraints)); 296 return this; 297 } 298 299 /** 300 * Request that an overlay package be self-targeting. Self-targeting overlays enable 301 * applications to overlay on itself resources. The overlay target is itself, or the Android 302 * package, and the work range is only in caller application. 303 * @param selfTargeting whether the overlay is self-targeting, the default is false. 304 * 305 * @hide 306 */ setSelfTargeting(boolean selfTargeting)307 public Builder setSelfTargeting(boolean selfTargeting) { 308 mSelfTargeting = selfTargeting; 309 return this; 310 } 311 312 /** 313 * Registers the fabricated overlay with the overlay manager so it can be enabled and 314 * disabled for any user. 315 * 316 * The fabricated overlay is initialized in a disabled state. If an overlay is re-registered 317 * the existing overlay will be replaced by the newly registered overlay and the enabled 318 * state of the overlay will be left unchanged if the target package and target overlayable 319 * have not changed. 320 * 321 * @param overlay the overlay to register with the overlay manager 322 * 323 * @hide 324 */ 325 @NonNull registerFabricatedOverlay(@onNull FabricatedOverlay overlay)326 public Builder registerFabricatedOverlay(@NonNull FabricatedOverlay overlay) { 327 mRequests.add(generateRegisterFabricatedOverlayRequest(overlay)); 328 return this; 329 } 330 331 /** 332 * Disables and removes the overlay from the overlay manager for all users. 333 * 334 * @param overlay the overlay to disable and remove 335 * 336 * @hide 337 */ 338 @NonNull unregisterFabricatedOverlay(@onNull OverlayIdentifier overlay)339 public Builder unregisterFabricatedOverlay(@NonNull OverlayIdentifier overlay) { 340 mRequests.add(generateUnRegisterFabricatedOverlayRequest(overlay)); 341 return this; 342 } 343 344 /** 345 * Create a new transaction out of the requests added so far. Execute 346 * the transaction by calling OverlayManager#commit. 347 * 348 * @see OverlayManager#commit 349 * @return a new transaction 350 */ 351 @NonNull build()352 public OverlayManagerTransaction build() { 353 return new OverlayManagerTransaction(mRequests, mSelfTargeting); 354 } 355 } 356 357 /** 358 * {@inheritDoc} 359 */ 360 @Override describeContents()361 public int describeContents() { 362 return 0; 363 } 364 365 /** 366 * {@inheritDoc} 367 */ 368 @Override writeToParcel(@onNull Parcel dest, int flags)369 public void writeToParcel(@NonNull Parcel dest, int flags) { 370 final int size = mRequests.size(); 371 dest.writeInt(size); 372 for (int i = 0; i < size; i++) { 373 final Request req = mRequests.get(i); 374 dest.writeInt(req.type); 375 dest.writeParcelable(req.overlay, flags); 376 dest.writeInt(req.userId); 377 dest.writeBundle(req.extras); 378 dest.writeTypedArray(req.constraints.toArray(new OverlayConstraint[0]), flags); 379 } 380 } 381 382 @NonNull 383 public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR = 384 new Parcelable.Creator<>() { 385 386 @Override 387 public OverlayManagerTransaction createFromParcel(Parcel source) { 388 return new OverlayManagerTransaction(source); 389 } 390 391 @Override 392 public OverlayManagerTransaction[] newArray(int size) { 393 return new OverlayManagerTransaction[size]; 394 } 395 }; 396 generateRegisterFabricatedOverlayRequest( @onNull FabricatedOverlay overlay)397 private static Request generateRegisterFabricatedOverlayRequest( 398 @NonNull FabricatedOverlay overlay) { 399 Objects.requireNonNull(overlay); 400 401 final Bundle extras = new Bundle(); 402 extras.putParcelable(Request.BUNDLE_FABRICATED_OVERLAY, overlay.mOverlay); 403 return new Request(Request.TYPE_REGISTER_FABRICATED, overlay.getIdentifier(), 404 UserHandle.USER_ALL, extras); 405 } 406 generateUnRegisterFabricatedOverlayRequest( @onNull OverlayIdentifier overlayIdentifier)407 private static Request generateUnRegisterFabricatedOverlayRequest( 408 @NonNull OverlayIdentifier overlayIdentifier) { 409 Objects.requireNonNull(overlayIdentifier); 410 411 return new Request(Request.TYPE_UNREGISTER_FABRICATED, overlayIdentifier, 412 UserHandle.USER_ALL); 413 } 414 415 /** 416 * Registers the fabricated overlays with the overlay manager so it can be used to overlay 417 * the app resources in runtime. 418 * 419 * <p>If an overlay is re-registered the existing overlay will be replaced by the newly 420 * registered overlay. The registered overlay will be left unchanged until the target 421 * package or target overlayable is changed. 422 * 423 * @param overlay the overlay to register with the overlay manager 424 */ 425 @NonNull registerFabricatedOverlay(@onNull FabricatedOverlay overlay)426 public void registerFabricatedOverlay(@NonNull FabricatedOverlay overlay) { 427 mRequests.add(generateRegisterFabricatedOverlayRequest(overlay)); 428 } 429 430 /** 431 * Unregisters the registered overlays from the overlay manager. 432 * 433 * @param overlay the overlay to be unregistered 434 * 435 * @see OverlayManager#getOverlayInfosForTarget(String) 436 * @see OverlayInfo#getOverlayIdentifier() 437 */ 438 @NonNull unregisterFabricatedOverlay(@onNull OverlayIdentifier overlay)439 public void unregisterFabricatedOverlay(@NonNull OverlayIdentifier overlay) { 440 mRequests.add(generateUnRegisterFabricatedOverlayRequest(overlay)); 441 } 442 443 /** 444 * Indicate whether the transaction is for self-targeting or not. 445 */ isSelfTargeting()446 boolean isSelfTargeting() { 447 return mSelfTargeting; 448 } 449 } 450