1 /*
2  * Copyright (C) 2014 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.telecom;
18 
19 import android.annotation.Nullable;
20 import android.annotation.SystemApi;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.graphics.drawable.Drawable;
24 import android.graphics.drawable.Icon;
25 import android.os.Binder;
26 import android.os.Bundle;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.os.UserHandle;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 
33 import java.util.Objects;
34 
35 /**
36  * Contains status label and icon displayed in the in-call UI.
37  */
38 public final class StatusHints implements Parcelable {
39 
40     private final CharSequence mLabel;
41     private Icon mIcon;
42     private final Bundle mExtras;
43 
44     /**
45      * @hide
46      */
47     @SystemApi @Deprecated
StatusHints(ComponentName packageName, CharSequence label, int iconResId, Bundle extras)48     public StatusHints(ComponentName packageName, CharSequence label, int iconResId,
49             Bundle extras) {
50         this(label, iconResId == 0 ? null : Icon.createWithResource(packageName.getPackageName(),
51             iconResId), extras);
52     }
53 
StatusHints(CharSequence label, Icon icon, Bundle extras)54     public StatusHints(CharSequence label, Icon icon, Bundle extras) {
55         mLabel = label;
56         mIcon = validateAccountIconUserBoundary(icon, Binder.getCallingUserHandle());
57         mExtras = extras;
58     }
59 
60     /**
61      * @param icon
62      * @hide
63      */
64     @VisibleForTesting
StatusHints(@ullable Icon icon)65     public StatusHints(@Nullable Icon icon) {
66         mLabel = null;
67         mExtras = null;
68         mIcon = icon;
69     }
70 
71     /**
72      *
73      * @param icon
74      * @hide
75      */
setIcon(@ullable Icon icon)76     public void setIcon(@Nullable Icon icon) {
77         mIcon = icon;
78     }
79 
80     /**
81      * @return A package used to load the icon.
82      *
83      * @hide
84      */
85     @SystemApi @Deprecated
getPackageName()86     public ComponentName getPackageName() {
87         // Minimal compatibility shim for legacy apps' tests
88         return new ComponentName("", "");
89     }
90 
91     /**
92      * @return The label displayed in the in-call UI.
93      */
getLabel()94     public CharSequence getLabel() {
95         return mLabel;
96     }
97 
98     /**
99      * The icon resource ID for the icon to show.
100      *
101      * @return A resource ID.
102      *
103      * @hide
104      */
105     @SystemApi @Deprecated
getIconResId()106     public int getIconResId() {
107         // Minimal compatibility shim for legacy apps' tests
108         return 0;
109     }
110 
111     /**
112      * @return An icon displayed in the in-call UI.
113      *
114      * @hide
115      */
116     @SystemApi @Deprecated
getIcon(Context context)117     public Drawable getIcon(Context context) {
118         return mIcon.loadDrawable(context);
119     }
120 
121     /**
122      * @return An icon depicting the status.
123      */
getIcon()124     public Icon getIcon() {
125         return mIcon;
126     }
127 
128     /**
129      * @return Extra data used to display status.
130      */
getExtras()131     public Bundle getExtras() {
132         return mExtras;
133     }
134 
135     @Override
describeContents()136     public int describeContents() {
137         return 0;
138     }
139 
140     /**
141      * Validates the StatusHints image icon to see if it's not in the calling user space.
142      * Invalidates the icon if so, otherwise returns back the original icon.
143      *
144      * @param icon
145      * @return icon (validated)
146      * @hide
147      */
validateAccountIconUserBoundary(Icon icon, UserHandle callingUserHandle)148     public static Icon validateAccountIconUserBoundary(Icon icon, UserHandle callingUserHandle) {
149         // Refer to Icon#getUriString for context. The URI string is invalid for icons of
150         // incompatible types.
151         if (icon != null && (icon.getType() == Icon.TYPE_URI
152                 || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
153             String encodedUser = icon.getUri().getEncodedUserInfo();
154             // If there is no encoded user, the URI is calling into the calling user space
155             if (encodedUser != null) {
156                 int userId = Integer.parseInt(encodedUser);
157                 // Do not try to save the icon if the user id isn't in the calling user space.
158                 if (userId != callingUserHandle.getIdentifier()) return null;
159             }
160         }
161         return icon;
162     }
163 
164     @Override
writeToParcel(Parcel out, int flags)165     public void writeToParcel(Parcel out, int flags) {
166         out.writeCharSequence(mLabel);
167         out.writeParcelable(mIcon, 0);
168         out.writeParcelable(mExtras, 0);
169     }
170 
171     public static final @android.annotation.NonNull Creator<StatusHints> CREATOR
172             = new Creator<StatusHints>() {
173         public StatusHints createFromParcel(Parcel in) {
174             return new StatusHints(in);
175         }
176 
177         public StatusHints[] newArray(int size) {
178             return new StatusHints[size];
179         }
180     };
181 
StatusHints(Parcel in)182     private StatusHints(Parcel in) {
183         mLabel = in.readCharSequence();
184         mIcon = in.readParcelable(getClass().getClassLoader(), android.graphics.drawable.Icon.class);
185         mExtras = in.readParcelable(getClass().getClassLoader(), android.os.Bundle.class);
186     }
187 
188     @Override
equals(Object other)189     public boolean equals(Object other) {
190         if (other != null && other instanceof StatusHints) {
191             StatusHints otherHints = (StatusHints) other;
192             return Objects.equals(otherHints.getLabel(), getLabel()) &&
193                     Objects.equals(otherHints.getIcon(), getIcon()) &&
194                     Objects.equals(otherHints.getExtras(), getExtras());
195         }
196         return false;
197     }
198 
199     @Override
hashCode()200     public int hashCode() {
201         return Objects.hashCode(mLabel) + Objects.hashCode(mIcon) + Objects.hashCode(mExtras);
202     }
203 }
204