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      */
389     @Override
390     @NonNull
getOverlayIdentifier()391     public OverlayIdentifier getOverlayIdentifier() {
392         if (mIdentifierCached == null) {
393             mIdentifierCached = new OverlayIdentifier(packageName, overlayName);
394         }
395         return mIdentifierCached;
396     }
397 
398     @SuppressWarnings("ConstantConditions")
ensureValidState()399     private void ensureValidState() {
400         if (packageName == null) {
401             throw new IllegalArgumentException("packageName must not be null");
402         }
403         if (targetPackageName == null) {
404             throw new IllegalArgumentException("targetPackageName must not be null");
405         }
406         if (baseCodePath == null) {
407             throw new IllegalArgumentException("baseCodePath must not be null");
408         }
409         switch (state) {
410             case STATE_UNKNOWN:
411             case STATE_MISSING_TARGET:
412             case STATE_NO_IDMAP:
413             case STATE_DISABLED:
414             case STATE_ENABLED:
415             case STATE_ENABLED_IMMUTABLE:
416             case STATE_TARGET_IS_BEING_REPLACED:
417             case STATE_OVERLAY_IS_BEING_REPLACED:
418                 break;
419             default:
420                 throw new IllegalArgumentException("State " + state + " is not a valid state");
421         }
422     }
423 
424     @Override
describeContents()425     public int describeContents() {
426         return 0;
427     }
428 
429     @Override
writeToParcel(@onNull Parcel dest, int flags)430     public void writeToParcel(@NonNull Parcel dest, int flags) {
431         dest.writeString(packageName);
432         dest.writeString(overlayName);
433         dest.writeString(targetPackageName);
434         dest.writeString(targetOverlayableName);
435         dest.writeString(category);
436         dest.writeString(baseCodePath);
437         dest.writeInt(state);
438         dest.writeInt(userId);
439         dest.writeInt(priority);
440         dest.writeBoolean(isMutable);
441         dest.writeBoolean(isFabricated);
442     }
443 
444     public static final @NonNull Parcelable.Creator<OverlayInfo> CREATOR =
445             new Parcelable.Creator<OverlayInfo>() {
446         @Override
447         public OverlayInfo createFromParcel(Parcel source) {
448             return new OverlayInfo(source);
449         }
450 
451         @Override
452         public OverlayInfo[] newArray(int size) {
453             return new OverlayInfo[size];
454         }
455     };
456 
457     /**
458      * Return true if this overlay is enabled, i.e. should be used to overlay
459      * the resources in the target package.
460      *
461      * Disabled overlay packages are installed but are currently not in use.
462      *
463      * @return true if the overlay is enabled, else false.
464      * @hide
465      */
466     @SystemApi
isEnabled()467     public boolean isEnabled() {
468         switch (state) {
469             case STATE_ENABLED:
470             case STATE_ENABLED_IMMUTABLE:
471                 return true;
472             default:
473                 return false;
474         }
475     }
476 
477     /**
478      * Translate a state to a human readable string. Only intended for
479      * debugging purposes.
480      *
481      * @return a human readable String representing the state.
482      * @hide
483      */
stateToString(@tate int state)484     public static String stateToString(@State int state) {
485         switch (state) {
486             case STATE_UNKNOWN:
487                 return "STATE_UNKNOWN";
488             case STATE_MISSING_TARGET:
489                 return "STATE_MISSING_TARGET";
490             case STATE_NO_IDMAP:
491                 return "STATE_NO_IDMAP";
492             case STATE_DISABLED:
493                 return "STATE_DISABLED";
494             case STATE_ENABLED:
495                 return "STATE_ENABLED";
496             case STATE_ENABLED_IMMUTABLE:
497                 return "STATE_ENABLED_IMMUTABLE";
498             case STATE_TARGET_IS_BEING_REPLACED:
499                 return "STATE_TARGET_IS_BEING_REPLACED";
500             case STATE_OVERLAY_IS_BEING_REPLACED:
501                 return "STATE_OVERLAY_IS_BEING_REPLACED";
502             default:
503                 return "<unknown state>";
504         }
505     }
506 
507     /**
508      * {@inheritDoc}
509      *
510      * @hide
511      */
512     @Override
hashCode()513     public int hashCode() {
514         final int prime = 31;
515         int result = 1;
516         result = prime * result + userId;
517         result = prime * result + state;
518         result = prime * result + ((packageName == null) ? 0 : packageName.hashCode());
519         result = prime * result + ((overlayName == null) ? 0 : overlayName.hashCode());
520         result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode());
521         result = prime * result + ((targetOverlayableName == null) ? 0
522                 : targetOverlayableName.hashCode());
523         result = prime * result + ((category == null) ? 0 : category.hashCode());
524         result = prime * result + ((baseCodePath == null) ? 0 : baseCodePath.hashCode());
525         return result;
526     }
527 
528     /**
529      * {@inheritDoc}
530      *
531      * @hide
532      */
533     @Override
equals(@ullable Object obj)534     public boolean equals(@Nullable Object obj) {
535         if (this == obj) {
536             return true;
537         }
538         if (obj == null) {
539             return false;
540         }
541         if (getClass() != obj.getClass()) {
542             return false;
543         }
544         OverlayInfo other = (OverlayInfo) obj;
545         if (userId != other.userId) {
546             return false;
547         }
548         if (state != other.state) {
549             return false;
550         }
551         if (!packageName.equals(other.packageName)) {
552             return false;
553         }
554         if (!Objects.equals(overlayName, other.overlayName)) {
555             return false;
556         }
557         if (!targetPackageName.equals(other.targetPackageName)) {
558             return false;
559         }
560         if (!Objects.equals(targetOverlayableName, other.targetOverlayableName)) {
561             return false;
562         }
563         if (!Objects.equals(category, other.category)) {
564             return false;
565         }
566         if (!baseCodePath.equals(other.baseCodePath)) {
567             return false;
568         }
569         return true;
570     }
571 
572     /**
573      * {@inheritDoc}
574      *
575      * @hide
576      */
577     @NonNull
578     @Override
toString()579     public String toString() {
580         return "OverlayInfo {"
581                 + "packageName=" + packageName
582                 + ", overlayName=" + overlayName
583                 + ", targetPackage=" + targetPackageName
584                 + ", targetOverlayable=" + targetOverlayableName
585                 + ", state=" + state + " (" + stateToString(state) + "),"
586                 + ", userId=" + userId
587                 + " }";
588     }
589 }
590