• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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