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