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