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.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.content.ComponentName;
23 import android.os.Build;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.os.Process;
27 import android.os.UserHandle;
28 
29 import java.util.Objects;
30 
31 /**
32  * The unique identifier for a {@link PhoneAccount}. A {@code PhoneAccountHandle} is made of two
33  * parts:
34  * <ul>
35  *  <li>The component name of the associated connection service.</li>
36  *  <li>A string identifier that is unique across {@code PhoneAccountHandle}s with the same
37  *      component name.</li>
38  * </ul>
39  *
40  * Note: This Class requires a non-null {@link ComponentName} and {@link UserHandle} to operate
41  * properly. Passing in invalid parameters will generate a log warning.
42  *
43  * See {@link PhoneAccount}, {@link TelecomManager}.
44  */
45 public final class PhoneAccountHandle implements Parcelable {
46     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 127403196)
47     private final ComponentName mComponentName;
48     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
49     private final String mId;
50     private final UserHandle mUserHandle;
51 
PhoneAccountHandle( @onNull ComponentName componentName, @NonNull String id)52     public PhoneAccountHandle(
53             @NonNull ComponentName componentName,
54             @NonNull String id) {
55         this(componentName, id, Process.myUserHandle());
56     }
57 
PhoneAccountHandle( @onNull ComponentName componentName, @NonNull String id, @NonNull UserHandle userHandle)58     public PhoneAccountHandle(
59             @NonNull ComponentName componentName,
60             @NonNull String id,
61             @NonNull UserHandle userHandle) {
62         checkParameters(componentName, userHandle);
63         mComponentName = componentName;
64         mId = id;
65         mUserHandle = userHandle;
66     }
67 
68     /**
69      * The {@code ComponentName} of the connection service which is responsible for making phone
70      * calls using this {@code PhoneAccountHandle}.
71      *
72      * @return A suitable {@code ComponentName}.
73      */
getComponentName()74     public ComponentName getComponentName() {
75         return mComponentName;
76     }
77 
78     /**
79      * A string that uniquely distinguishes this particular {@code PhoneAccountHandle} from all the
80      * others supported by the connection service that created it.
81      * <p>
82      * A connection service must select identifiers that are stable for the lifetime of
83      * their users' relationship with their service, across many Android devices. For example, a
84      * good set of identifiers might be the email addresses with which with users registered for
85      * their accounts with a particular service. Depending on how a service chooses to operate,
86      * a bad set of identifiers might be an increasing series of integers
87      * ({@code 0}, {@code 1}, {@code 2}, ...) that are generated locally on each phone and could
88      * collide with values generated on other phones or after a data wipe of a given phone.
89      *
90      * Important: A non-unique identifier could cause non-deterministic call-log backup/restore
91      * behavior.
92      *
93      * @return A service-specific unique identifier for this {@code PhoneAccountHandle}.
94      */
getId()95     public String getId() {
96         return mId;
97     }
98 
99     /**
100      * @return the {@link UserHandle} to use when connecting to this PhoneAccount.
101      */
getUserHandle()102     public UserHandle getUserHandle() {
103         return mUserHandle;
104     }
105 
106     @Override
hashCode()107     public int hashCode() {
108         return Objects.hash(mComponentName, mId, mUserHandle);
109     }
110 
111     @Override
toString()112     public String toString() {
113         // Note: Log.pii called for mId as it can contain personally identifying phone account
114         // information such as SIP account IDs.
115         return new StringBuilder().append(mComponentName)
116                     .append(", ")
117                     .append(Log.pii(mId))
118                     .append(", ")
119                     .append(mUserHandle)
120                     .toString();
121     }
122 
123     @Override
equals(Object other)124     public boolean equals(Object other) {
125         return other != null &&
126                 other instanceof PhoneAccountHandle &&
127                 Objects.equals(((PhoneAccountHandle) other).getComponentName(),
128                         getComponentName()) &&
129                 Objects.equals(((PhoneAccountHandle) other).getId(), getId()) &&
130                 Objects.equals(((PhoneAccountHandle) other).getUserHandle(), getUserHandle());
131     }
132 
133     //
134     // Parcelable implementation.
135     //
136 
137     @Override
describeContents()138     public int describeContents() {
139         return 0;
140     }
141 
142     @Override
writeToParcel(Parcel out, int flags)143     public void writeToParcel(Parcel out, int flags) {
144         mComponentName.writeToParcel(out, flags);
145         out.writeString(mId);
146         mUserHandle.writeToParcel(out, flags);
147     }
148 
checkParameters(ComponentName componentName, UserHandle userHandle)149     private void checkParameters(ComponentName componentName, UserHandle userHandle) {
150         if(componentName == null) {
151             android.util.Log.w("PhoneAccountHandle", new Exception("PhoneAccountHandle has " +
152                     "been created with null ComponentName!"));
153         }
154         if(userHandle == null) {
155             android.util.Log.w("PhoneAccountHandle", new Exception("PhoneAccountHandle has " +
156                     "been created with null UserHandle!"));
157         }
158     }
159 
160     public static final @android.annotation.NonNull Creator<PhoneAccountHandle> CREATOR = new Creator<PhoneAccountHandle>() {
161         @Override
162         public PhoneAccountHandle createFromParcel(Parcel in) {
163             return new PhoneAccountHandle(in);
164         }
165 
166         @Override
167         public PhoneAccountHandle[] newArray(int size) {
168             return new PhoneAccountHandle[size];
169         }
170     };
171 
172     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
PhoneAccountHandle(Parcel in)173     private PhoneAccountHandle(Parcel in) {
174         this(ComponentName.CREATOR.createFromParcel(in),
175                 in.readString(),
176                 UserHandle.CREATOR.createFromParcel(in));
177     }
178 
179     /**
180      * Determines if two {@link PhoneAccountHandle}s are from the same package.
181      *
182      * @param a Phone account handle to check for same {@link ConnectionService} package.
183      * @param b Other phone account handle to check for same {@link ConnectionService} package.
184      * @return {@code true} if the two {@link PhoneAccountHandle}s passed in belong to the same
185      * {@link ConnectionService} / package, {@code false} otherwise.  Note: {@code null} phone
186      * account handles are considered equivalent to other {@code null} phone account handles.
187      * @hide
188      */
areFromSamePackage(@ullable PhoneAccountHandle a, @Nullable PhoneAccountHandle b)189     public static boolean areFromSamePackage(@Nullable PhoneAccountHandle a,
190             @Nullable PhoneAccountHandle b) {
191         String aPackageName = a != null ? a.getComponentName().getPackageName() : null;
192         String bPackageName = b != null ? b.getComponentName().getPackageName() : null;
193         return Objects.equals(aPackageName, bPackageName);
194     }
195 }
196