1 /*
2  * Copyright (C) 2015 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 android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.annotation.UserIdInt;
24 import android.compat.annotation.UnsupportedAppUsage;
25 import android.os.Build;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.RetentionPolicy;
33 import java.util.Objects;
34 
35 /**
36  * An immutable information about an overlay.
37  *
38  * <p>Applications calling {@link OverlayManager#getOverlayInfosForTarget(String)} get the
39  * information list of the registered overlays. Each element in the list presents the information of
40  * the particular overlay.
41  *
42  * <!-- For OverlayManagerService, it isn't public part and hidden by HTML comment. -->
43  * <!--
44  * Immutable overlay information about a package. All PackageInfos that
45  * represent an overlay package will have a corresponding OverlayInfo.
46  * -->
47  *
48  * @see OverlayManager#getOverlayInfosForTarget(String)
49  */
50 public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
51 
52     /** @hide */
53     @IntDef(prefix = "STATE_", value = {
54             STATE_UNKNOWN,
55             STATE_MISSING_TARGET,
56             STATE_NO_IDMAP,
57             STATE_DISABLED,
58             STATE_ENABLED,
59             STATE_ENABLED_IMMUTABLE,
60             STATE_OVERLAY_IS_BEING_REPLACED,
61             STATE_SYSTEM_UPDATE_UNINSTALL,
62     })
63     /** @hide */
64     @Retention(RetentionPolicy.SOURCE)
65     public @interface State {}
66 
67     /**
68      * An internal state used as the initial state of an overlay. OverlayInfo
69      * objects exposed outside the {@link
70      * com.android.server.om.OverlayManagerService} should never have this
71      * state.
72      *
73      * @hide
74      */
75     public static final int STATE_UNKNOWN = -1;
76 
77     /**
78      * The target package of the overlay is not installed. The overlay cannot be enabled.
79      *
80      * @hide
81      */
82     public static final int STATE_MISSING_TARGET = 0;
83 
84     /**
85      * Creation of idmap file failed (e.g. no matching resources). The overlay
86      * cannot be enabled.
87      *
88      * @hide
89      */
90     public static final int STATE_NO_IDMAP = 1;
91 
92     /**
93      * The overlay is currently disabled. It can be enabled.
94      *
95      * @see IOverlayManager#setEnabled
96      * @hide
97      */
98     public static final int STATE_DISABLED = 2;
99 
100     /**
101      * The overlay is currently enabled. It can be disabled.
102      *
103      * @see IOverlayManager#setEnabled
104      * @hide
105      */
106     public static final int STATE_ENABLED = 3;
107 
108     /**
109      * The target package is currently being upgraded or downgraded; the state
110      * will change once the package installation has finished.
111      * @hide
112      *
113      * @deprecated No longer used. Caused invalid transitions from enabled -> upgrading -> enabled,
114      * where an update is propagated when nothing has changed. Can occur during --dont-kill
115      * installs when code and resources are hot swapped and the Activity should not be relaunched.
116      * In all other cases, the process and therefore Activity is killed, so the state loop is
117      * irrelevant.
118      */
119     @Deprecated
120     public static final int STATE_TARGET_IS_BEING_REPLACED = 4;
121 
122     /**
123      * The overlay package is currently being upgraded or downgraded; the state
124      * will change once the package installation has finished.
125      * @hide
126      */
127     public static final int STATE_OVERLAY_IS_BEING_REPLACED = 5;
128 
129     /**
130      * The overlay package is currently enabled because it is marked as
131      * 'immutable'. It cannot be disabled but will change state if for instance
132      * its target is uninstalled.
133      * @hide
134      */
135     @Deprecated
136     public static final int STATE_ENABLED_IMMUTABLE = 6;
137 
138     /**
139      * The target package needs to be refreshed as a result of a system update uninstall, which
140      * must recalculate the state of overlays against the newly enabled system package, which may
141      * differ in resources/policy from the /data variant that was uninstalled.
142      * @hide
143      */
144     public static final int STATE_SYSTEM_UPDATE_UNINSTALL = 7;
145 
146     /**
147      * Overlay category: theme.
148      * <p>
149      * Change how Android (including the status bar, dialogs, ...) looks.
150      *
151      * @hide
152      */
153     public static final String CATEGORY_THEME = "android.theme";
154 
155     /**
156      * Package name of the overlay package
157      *
158      * @hide
159      */
160     @NonNull
161     public final String packageName;
162 
163     /**
164      * The unique name within the package of the overlay.
165      *
166      * @hide
167      */
168     @Nullable
169     public final String overlayName;
170 
171     /**
172      * Package name of the target package
173      *
174      * @hide
175      */
176     @NonNull
177     public final String targetPackageName;
178 
179     /**
180      * Name of the target overlayable declaration.
181      *
182      * @hide
183      */
184     @Nullable public final String targetOverlayableName;
185 
186     /**
187      * Category of the overlay package
188      *
189      * @hide
190      */
191     @Nullable public final String category;
192 
193     /**
194      * Full path to the base APK for this overlay package
195      * @hide
196      */
197     @NonNull
198     public final String baseCodePath;
199 
200     /**
201      * The state of this OverlayInfo as defined by the STATE_* constants in this class.
202      * @hide
203      */
204     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
205     public final @State int state;
206 
207     /**
208      * User handle for which this overlay applies
209      * @hide
210      */
211     public final int userId;
212 
213     /**
214      * Priority as configured by {@link com.android.internal.content.om.OverlayConfig}.
215      * Not intended to be exposed to 3rd party.
216      *
217      * @hide
218      */
219     public final int priority;
220 
221     /**
222      * isMutable as configured by {@link com.android.internal.content.om.OverlayConfig}.
223      * If false, the overlay is unconditionally loaded and cannot be unloaded. Not intended to be
224      * exposed to 3rd party.
225      *
226      * @hide
227      */
228     public final boolean isMutable;
229 
230     private OverlayIdentifier mIdentifierCached;
231 
232     /**
233      *
234      * @hide
235      */
236     public final boolean isFabricated;
237 
238     /**
239      * Create a new OverlayInfo based on source with an updated state.
240      *
241      * @param source the source OverlayInfo to base the new instance on
242      * @param state the new state for the source OverlayInfo
243      *
244      * @hide
245      */
OverlayInfo(@onNull OverlayInfo source, @State int state)246     public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
247         this(source.packageName, source.overlayName, source.targetPackageName,
248                 source.targetOverlayableName, source.category, source.baseCodePath, state,
249                 source.userId, source.priority, source.isMutable, source.isFabricated);
250     }
251 
252     /** @hide */
253     @VisibleForTesting
OverlayInfo(@onNull String packageName, @NonNull String targetPackageName, @Nullable String targetOverlayableName, @Nullable String category, @NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable)254     public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
255             @Nullable String targetOverlayableName, @Nullable String category,
256             @NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable) {
257         this(packageName, null /* overlayName */, targetPackageName, targetOverlayableName,
258                 category, baseCodePath, state, userId, priority, isMutable,
259                 false /* isFabricated */);
260     }
261 
262     /** @hide */
OverlayInfo(@onNull String packageName, @Nullable String overlayName, @NonNull String targetPackageName, @Nullable String targetOverlayableName, @Nullable String category, @NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable, boolean isFabricated)263     public OverlayInfo(@NonNull String packageName, @Nullable String overlayName,
264             @NonNull String targetPackageName, @Nullable String targetOverlayableName,
265             @Nullable String category, @NonNull String baseCodePath, int state, int userId,
266             int priority, boolean isMutable, boolean isFabricated) {
267         this.packageName = packageName;
268         this.overlayName = overlayName;
269         this.targetPackageName = targetPackageName;
270         this.targetOverlayableName = targetOverlayableName;
271         this.category = category;
272         this.baseCodePath = baseCodePath;
273         this.state = state;
274         this.userId = userId;
275         this.priority = priority;
276         this.isMutable = isMutable;
277         this.isFabricated = isFabricated;
278         ensureValidState();
279     }
280 
281     /** @hide */
OverlayInfo(@onNull Parcel source)282     public OverlayInfo(@NonNull Parcel source) {
283         packageName = source.readString();
284         overlayName = source.readString();
285         targetPackageName = source.readString();
286         targetOverlayableName = source.readString();
287         category = source.readString();
288         baseCodePath = source.readString();
289         state = source.readInt();
290         userId = source.readInt();
291         priority = source.readInt();
292         isMutable = source.readBoolean();
293         isFabricated = source.readBoolean();
294         ensureValidState();
295     }
296 
297     /**
298      * {@inheritDoc}
299      * @hide
300      */
301     @Override
302     @SystemApi
303     @NonNull
getPackageName()304     public String getPackageName() {
305         return packageName;
306     }
307 
308     /**
309      * Get the overlay name from the registered fabricated overlay.
310      *
311      * @return the overlay name
312      */
313     @Override
314     @Nullable
getOverlayName()315     public String getOverlayName() {
316         return overlayName;
317     }
318 
319     /**
320      * Returns the name of the target overlaid package.
321      *
322      * @return the target package name
323      */
324     @Override
325     @NonNull
getTargetPackageName()326     public String getTargetPackageName() {
327         return targetPackageName;
328     }
329 
330     /**
331      * Returns the category of the current overlay.
332      *
333      * @hide
334      */
335     @SystemApi
336     @Nullable
getCategory()337     public String getCategory() {
338         return category;
339     }
340 
341     /**
342      * Returns user handle for which this overlay applies to.
343      *
344      * @hide
345      */
346     @SystemApi
347     @UserIdInt
getUserId()348     public int getUserId() {
349         return userId;
350     }
351 
352     /**
353      * Return the target overlayable name.
354      *
355      * @return the name of the target overlayable resources set
356      */
357     @Override
358     @Nullable
getTargetOverlayableName()359     public String getTargetOverlayableName() {
360         return targetOverlayableName;
361     }
362 
363     /**
364      * {@inheritDoc}
365      * @hide
366      */
367     @Override
isFabricated()368     public boolean isFabricated() {
369         return isFabricated;
370     }
371 
372     /**
373      * Full path to the base APK or fabricated overlay for this overlay package.
374      *
375      * @hide
376      */
377     @NonNull
getBaseCodePath()378     public String getBaseCodePath() {
379         return baseCodePath;
380     }
381 
382     /**
383      * Get the unique identifier from the overlay information.
384      *
385      * <p>The return value of this function can be used to unregister the related overlay.
386      *
387      * @return an identifier representing the current overlay.
388      * @see OverlayManagerTransaction.Builder#unregisterFabricatedOverlay(OverlayIdentifier)
389      */
390     @Override
391     @NonNull
getOverlayIdentifier()392     public OverlayIdentifier getOverlayIdentifier() {
393         if (mIdentifierCached == null) {
394             mIdentifierCached = new OverlayIdentifier(packageName, overlayName);
395         }
396         return mIdentifierCached;
397     }
398 
399     @SuppressWarnings("ConstantConditions")
ensureValidState()400     private void ensureValidState() {
401         if (packageName == null) {
402             throw new IllegalArgumentException("packageName must not be null");
403         }
404         if (targetPackageName == null) {
405             throw new IllegalArgumentException("targetPackageName must not be null");
406         }
407         if (baseCodePath == null) {
408             throw new IllegalArgumentException("baseCodePath must not be null");
409         }
410         switch (state) {
411             case STATE_UNKNOWN:
412             case STATE_MISSING_TARGET:
413             case STATE_NO_IDMAP:
414             case STATE_DISABLED:
415             case STATE_ENABLED:
416             case STATE_ENABLED_IMMUTABLE:
417             case STATE_TARGET_IS_BEING_REPLACED:
418             case STATE_OVERLAY_IS_BEING_REPLACED:
419                 break;
420             default:
421                 throw new IllegalArgumentException("State " + state + " is not a valid state");
422         }
423     }
424 
425     @Override
describeContents()426     public int describeContents() {
427         return 0;
428     }
429 
430     @Override
writeToParcel(@onNull Parcel dest, int flags)431     public void writeToParcel(@NonNull Parcel dest, int flags) {
432         dest.writeString(packageName);
433         dest.writeString(overlayName);
434         dest.writeString(targetPackageName);
435         dest.writeString(targetOverlayableName);
436         dest.writeString(category);
437         dest.writeString(baseCodePath);
438         dest.writeInt(state);
439         dest.writeInt(userId);
440         dest.writeInt(priority);
441         dest.writeBoolean(isMutable);
442         dest.writeBoolean(isFabricated);
443     }
444 
445     public static final @NonNull Parcelable.Creator<OverlayInfo> CREATOR =
446             new Parcelable.Creator<OverlayInfo>() {
447         @Override
448         public OverlayInfo createFromParcel(Parcel source) {
449             return new OverlayInfo(source);
450         }
451 
452         @Override
453         public OverlayInfo[] newArray(int size) {
454             return new OverlayInfo[size];
455         }
456     };
457 
458     /**
459      * Return true if this overlay is enabled, i.e. should be used to overlay
460      * the resources in the target package.
461      *
462      * Disabled overlay packages are installed but are currently not in use.
463      *
464      * @return true if the overlay is enabled, else false.
465      * @hide
466      */
467     @SystemApi
isEnabled()468     public boolean isEnabled() {
469         switch (state) {
470             case STATE_ENABLED:
471             case STATE_ENABLED_IMMUTABLE:
472                 return true;
473             default:
474                 return false;
475         }
476     }
477 
478     /**
479      * Translate a state to a human readable string. Only intended for
480      * debugging purposes.
481      *
482      * @return a human readable String representing the state.
483      * @hide
484      */
stateToString(@tate int state)485     public static String stateToString(@State int state) {
486         switch (state) {
487             case STATE_UNKNOWN:
488                 return "STATE_UNKNOWN";
489             case STATE_MISSING_TARGET:
490                 return "STATE_MISSING_TARGET";
491             case STATE_NO_IDMAP:
492                 return "STATE_NO_IDMAP";
493             case STATE_DISABLED:
494                 return "STATE_DISABLED";
495             case STATE_ENABLED:
496                 return "STATE_ENABLED";
497             case STATE_ENABLED_IMMUTABLE:
498                 return "STATE_ENABLED_IMMUTABLE";
499             case STATE_TARGET_IS_BEING_REPLACED:
500                 return "STATE_TARGET_IS_BEING_REPLACED";
501             case STATE_OVERLAY_IS_BEING_REPLACED:
502                 return "STATE_OVERLAY_IS_BEING_REPLACED";
503             default:
504                 return "<unknown state>";
505         }
506     }
507 
508     /**
509      * {@inheritDoc}
510      *
511      * @hide
512      */
513     @Override
hashCode()514     public int hashCode() {
515         final int prime = 31;
516         int result = 1;
517         result = prime * result + userId;
518         result = prime * result + state;
519         result = prime * result + ((packageName == null) ? 0 : packageName.hashCode());
520         result = prime * result + ((overlayName == null) ? 0 : overlayName.hashCode());
521         result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode());
522         result = prime * result + ((targetOverlayableName == null) ? 0
523                 : targetOverlayableName.hashCode());
524         result = prime * result + ((category == null) ? 0 : category.hashCode());
525         result = prime * result + ((baseCodePath == null) ? 0 : baseCodePath.hashCode());
526         return result;
527     }
528 
529     /**
530      * {@inheritDoc}
531      *
532      * @hide
533      */
534     @Override
equals(@ullable Object obj)535     public boolean equals(@Nullable Object obj) {
536         if (this == obj) {
537             return true;
538         }
539         if (obj == null) {
540             return false;
541         }
542         if (getClass() != obj.getClass()) {
543             return false;
544         }
545         OverlayInfo other = (OverlayInfo) obj;
546         if (userId != other.userId) {
547             return false;
548         }
549         if (state != other.state) {
550             return false;
551         }
552         if (!packageName.equals(other.packageName)) {
553             return false;
554         }
555         if (!Objects.equals(overlayName, other.overlayName)) {
556             return false;
557         }
558         if (!targetPackageName.equals(other.targetPackageName)) {
559             return false;
560         }
561         if (!Objects.equals(targetOverlayableName, other.targetOverlayableName)) {
562             return false;
563         }
564         if (!Objects.equals(category, other.category)) {
565             return false;
566         }
567         if (!baseCodePath.equals(other.baseCodePath)) {
568             return false;
569         }
570         return true;
571     }
572 
573     /**
574      * {@inheritDoc}
575      *
576      * @hide
577      */
578     @NonNull
579     @Override
toString()580     public String toString() {
581         return "OverlayInfo {"
582                 + "packageName=" + packageName
583                 + ", overlayName=" + overlayName
584                 + ", targetPackage=" + targetPackageName
585                 + ", targetOverlayable=" + targetOverlayableName
586                 + ", state=" + state + " (" + stateToString(state) + "),"
587                 + ", userId=" + userId
588                 + " }";
589     }
590 }
591