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 com.android.internal.util.Preconditions.checkNotNull; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.os.Bundle; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 import android.os.UserHandle; 29 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 import java.util.ArrayList; 33 import java.util.Iterator; 34 import java.util.List; 35 import java.util.Locale; 36 37 /** 38 * Container for a batch of requests to the OverlayManagerService. 39 * 40 * Transactions are created using a builder interface. Example usage: 41 * 42 * final OverlayManager om = ctx.getSystemService(OverlayManager.class); 43 * final OverlayManagerTransaction t = new OverlayManagerTransaction.Builder() 44 * .setEnabled(...) 45 * .setEnabled(...) 46 * .build(); 47 * om.commit(t); 48 * 49 * @hide 50 */ 51 public class OverlayManagerTransaction 52 implements Iterable<OverlayManagerTransaction.Request>, Parcelable { 53 // TODO: remove @hide from this class when OverlayManager is added to the 54 // SDK, but keep OverlayManagerTransaction.Request @hidden 55 private final List<Request> mRequests; 56 OverlayManagerTransaction(@onNull final List<Request> requests)57 OverlayManagerTransaction(@NonNull final List<Request> requests) { 58 checkNotNull(requests); 59 if (requests.contains(null)) { 60 throw new IllegalArgumentException("null request"); 61 } 62 mRequests = requests; 63 } 64 OverlayManagerTransaction(@onNull final Parcel source)65 private OverlayManagerTransaction(@NonNull final Parcel source) { 66 final int size = source.readInt(); 67 mRequests = new ArrayList<>(size); 68 for (int i = 0; i < size; i++) { 69 final int request = source.readInt(); 70 final OverlayIdentifier overlay = source.readParcelable(null); 71 final int userId = source.readInt(); 72 final Bundle extras = source.readBundle(null); 73 mRequests.add(new Request(request, overlay, userId, extras)); 74 } 75 } 76 77 @Override iterator()78 public Iterator<Request> iterator() { 79 return mRequests.iterator(); 80 } 81 82 @Override toString()83 public String toString() { 84 return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests); 85 } 86 87 /** 88 * A single unit of the transaction, such as a request to enable an 89 * overlay, or to disable an overlay. 90 * 91 * @hide 92 */ 93 public static class Request { 94 @IntDef(prefix = "TYPE_", value = { 95 TYPE_SET_ENABLED, 96 TYPE_SET_DISABLED, 97 }) 98 @Retention(RetentionPolicy.SOURCE) 99 @interface RequestType {} 100 101 public static final int TYPE_SET_ENABLED = 0; 102 public static final int TYPE_SET_DISABLED = 1; 103 public static final int TYPE_REGISTER_FABRICATED = 2; 104 public static final int TYPE_UNREGISTER_FABRICATED = 3; 105 106 public static final String BUNDLE_FABRICATED_OVERLAY = "fabricated_overlay"; 107 108 @RequestType 109 public final int type; 110 @NonNull 111 public final OverlayIdentifier overlay; 112 public final int userId; 113 @Nullable 114 public final Bundle extras; 115 Request(@equestType final int type, @NonNull final OverlayIdentifier overlay, final int userId)116 public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, 117 final int userId) { 118 this(type, overlay, userId, null /* extras */); 119 } 120 Request(@equestType final int type, @NonNull final OverlayIdentifier overlay, final int userId, @Nullable Bundle extras)121 public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, 122 final int userId, @Nullable Bundle extras) { 123 this.type = type; 124 this.overlay = overlay; 125 this.userId = userId; 126 this.extras = extras; 127 } 128 129 @Override toString()130 public String toString() { 131 return String.format(Locale.US, "Request{type=0x%02x (%s), overlay=%s, userId=%d}", 132 type, typeToString(), overlay, userId); 133 } 134 135 /** 136 * Translate the request type into a human readable string. Only 137 * intended for debugging. 138 * 139 * @hide 140 */ typeToString()141 public String typeToString() { 142 switch (type) { 143 case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED"; 144 case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED"; 145 case TYPE_REGISTER_FABRICATED: return "TYPE_REGISTER_FABRICATED"; 146 case TYPE_UNREGISTER_FABRICATED: return "TYPE_UNREGISTER_FABRICATED"; 147 default: return String.format("TYPE_UNKNOWN (0x%02x)", type); 148 } 149 } 150 } 151 152 /** 153 * Builder class for OverlayManagerTransaction objects. 154 * 155 * @hide 156 */ 157 public static class Builder { 158 private final List<Request> mRequests = new ArrayList<>(); 159 160 /** 161 * Request that an overlay package be enabled and change its loading 162 * order to the last package to be loaded, or disabled 163 * 164 * If the caller has the correct permissions, it is always possible to 165 * disable an overlay. Due to technical and security reasons it may not 166 * always be possible to enable an overlay, for instance if the overlay 167 * does not successfully overlay any target resources due to 168 * overlayable policy restrictions. 169 * 170 * An enabled overlay is a part of target package's resources, i.e. it will 171 * be part of any lookups performed via {@link android.content.res.Resources} 172 * and {@link android.content.res.AssetManager}. A disabled overlay will no 173 * longer affect the resources of the target package. If the target is 174 * currently running, its outdated resources will be replaced by new ones. 175 * 176 * @param overlay The name of the overlay package. 177 * @param enable true to enable the overlay, false to disable it. 178 * @return this Builder object, so you can chain additional requests 179 */ setEnabled(@onNull OverlayIdentifier overlay, boolean enable)180 public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable) { 181 return setEnabled(overlay, enable, UserHandle.myUserId()); 182 } 183 184 /** 185 * @hide 186 */ setEnabled(@onNull OverlayIdentifier overlay, boolean enable, int userId)187 public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, int userId) { 188 checkNotNull(overlay); 189 @Request.RequestType final int type = 190 enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED; 191 mRequests.add(new Request(type, overlay, userId)); 192 return this; 193 } 194 195 /** 196 * Registers the fabricated overlay with the overlay manager so it can be enabled and 197 * disabled for any user. 198 * 199 * The fabricated overlay is initialized in a disabled state. If an overlay is re-registered 200 * the existing overlay will be replaced by the newly registered overlay and the enabled 201 * state of the overlay will be left unchanged if the target package and target overlayable 202 * have not changed. 203 * 204 * @param overlay the overlay to register with the overlay manager 205 * 206 * @hide 207 */ registerFabricatedOverlay(@onNull FabricatedOverlay overlay)208 public Builder registerFabricatedOverlay(@NonNull FabricatedOverlay overlay) { 209 final Bundle extras = new Bundle(); 210 extras.putParcelable(Request.BUNDLE_FABRICATED_OVERLAY, overlay.mOverlay); 211 mRequests.add(new Request(Request.TYPE_REGISTER_FABRICATED, overlay.getIdentifier(), 212 UserHandle.USER_ALL, extras)); 213 return this; 214 } 215 216 /** 217 * Disables and removes the overlay from the overlay manager for all users. 218 * 219 * @param overlay the overlay to disable and remove 220 * 221 * @hide 222 */ unregisterFabricatedOverlay(@onNull OverlayIdentifier overlay)223 public Builder unregisterFabricatedOverlay(@NonNull OverlayIdentifier overlay) { 224 mRequests.add(new Request(Request.TYPE_UNREGISTER_FABRICATED, overlay, 225 UserHandle.USER_ALL)); 226 return this; 227 } 228 229 /** 230 * Create a new transaction out of the requests added so far. Execute 231 * the transaction by calling OverlayManager#commit. 232 * 233 * @see OverlayManager#commit 234 * @return a new transaction 235 */ build()236 public OverlayManagerTransaction build() { 237 return new OverlayManagerTransaction(mRequests); 238 } 239 } 240 241 @Override describeContents()242 public int describeContents() { 243 return 0; 244 } 245 246 @Override writeToParcel(Parcel dest, int flags)247 public void writeToParcel(Parcel dest, int flags) { 248 final int size = mRequests.size(); 249 dest.writeInt(size); 250 for (int i = 0; i < size; i++) { 251 final Request req = mRequests.get(i); 252 dest.writeInt(req.type); 253 dest.writeParcelable(req.overlay, flags); 254 dest.writeInt(req.userId); 255 dest.writeBundle(req.extras); 256 } 257 } 258 259 public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR = 260 new Parcelable.Creator<OverlayManagerTransaction>() { 261 262 @Override 263 public OverlayManagerTransaction createFromParcel(Parcel source) { 264 return new OverlayManagerTransaction(source); 265 } 266 267 @Override 268 public OverlayManagerTransaction[] newArray(int size) { 269 return new OverlayManagerTransaction[size]; 270 } 271 }; 272 } 273