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. Apps registering {@link PhoneAccountHandle}s should ensure that the
38  *      {@link #getId()} provided does not expose personally identifying information.  A
39  *      {@link ConnectionService} should use an opaque token as the {@link PhoneAccountHandle}
40  *      identifier.</li>
41  * </ul>
42  *
43  * Note: This Class requires a non-null {@link ComponentName} and {@link UserHandle} to operate
44  * properly. Passing in invalid parameters will generate a log warning.
45  *
46  * See {@link PhoneAccount}, {@link TelecomManager}.
47  */
48 public final class PhoneAccountHandle implements Parcelable {
49     /**
50      * Expected component name of Telephony phone accounts; ONLY used to determine if we should log
51      * the phone account handle ID.
52      */
53     private static final ComponentName TELEPHONY_COMPONENT_NAME =
54             new ComponentName("com.android.phone",
55                     "com.android.services.telephony.TelephonyConnectionService");
56 
57     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 127403196)
58     private final ComponentName mComponentName;
59     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
60     private final String mId;
61     private final UserHandle mUserHandle;
62 
63     /**
64      * Creates a new {@link PhoneAccountHandle}.
65      *
66      * @param componentName The {@link ComponentName} of the {@link ConnectionService} which
67      *                      services this {@link PhoneAccountHandle}.
68      * @param id A string identifier that is unique across {@code PhoneAccountHandle}s with the same
69      *           component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
70      *           ID provided does not expose personally identifying information.  A
71      *           {@link ConnectionService} should use an opaque token as the
72      *           {@link PhoneAccountHandle} identifier.
73      * <p>
74      * Note: Each String field is limited to 256 characters. This check is enforced when
75      *           registering the PhoneAccount via
76      *           {@link TelecomManager#registerPhoneAccount(PhoneAccount)} and will cause an
77      *           {@link IllegalArgumentException} to be thrown if the character field limit is
78      *           over 256.
79      */
PhoneAccountHandle( @onNull ComponentName componentName, @NonNull String id)80     public PhoneAccountHandle(
81             @NonNull ComponentName componentName,
82             @NonNull String id) {
83         this(componentName, id, Process.myUserHandle());
84     }
85 
86     /**
87      * Creates a new {@link PhoneAccountHandle}.
88      *
89      * @param componentName The {@link ComponentName} of the {@link ConnectionService} which
90      *                      services this {@link PhoneAccountHandle}.
91      * @param id A string identifier that is unique across {@code PhoneAccountHandle}s with the same
92      *           component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
93      *           ID provided does not expose personally identifying information.  A
94      *           {@link ConnectionService} should use an opaque token as the
95      *           {@link PhoneAccountHandle} identifier.
96      * @param userHandle The {@link UserHandle} associated with this {@link PhoneAccountHandle}.
97      *
98      * <p>
99      * Note: Each String field is limited to 256 characters. This check is enforced when
100      *           registering the PhoneAccount via
101      *           {@link TelecomManager#registerPhoneAccount(PhoneAccount)} and will cause an
102      *           {@link IllegalArgumentException} to be thrown if the character field limit is
103      *           over 256.
104      */
PhoneAccountHandle( @onNull ComponentName componentName, @NonNull String id, @NonNull UserHandle userHandle)105     public PhoneAccountHandle(
106             @NonNull ComponentName componentName,
107             @NonNull String id,
108             @NonNull UserHandle userHandle) {
109         checkParameters(componentName, userHandle);
110         mComponentName = componentName;
111         mId = id;
112         mUserHandle = userHandle;
113     }
114 
115     /**
116      * The {@code ComponentName} of the connection service which is responsible for making phone
117      * calls using this {@code PhoneAccountHandle}.
118      *
119      * @return A suitable {@code ComponentName}.
120      */
getComponentName()121     public ComponentName getComponentName() {
122         return mComponentName;
123     }
124 
125     /**
126      * A string that uniquely distinguishes this particular {@code PhoneAccountHandle} from all the
127      * others supported by the connection service that created it.
128      * <p>
129      * A connection service must select identifiers that are stable for the lifetime of
130      * their users' relationship with their service, across many Android devices.  The identifier
131      * should be a stable opaque token which uniquely identifies the user within the service.
132      * Depending on how a service chooses to operate, a bad set of identifiers might be an
133      * increasing series of integers ({@code 0}, {@code 1}, {@code 2}, ...) that are generated
134      * locally on each phone and could collide with values generated on other phones or after a data
135      * wipe of a given phone.
136      * <p>
137      * Important: A non-unique identifier could cause non-deterministic call-log backup/restore
138      * behavior.
139      *
140      * @return A service-specific unique opaque identifier for this {@code PhoneAccountHandle}.
141      */
getId()142     public String getId() {
143         return mId;
144     }
145 
146     /**
147      * @return the {@link UserHandle} to use when connecting to this PhoneAccount.
148      */
getUserHandle()149     public UserHandle getUserHandle() {
150         return mUserHandle;
151     }
152 
153     @Override
hashCode()154     public int hashCode() {
155         return Objects.hash(mComponentName, mId, mUserHandle);
156     }
157 
158     @Override
toString()159     public String toString() {
160         StringBuilder sb = new StringBuilder()
161                 .append(mComponentName)
162                 .append(", ");
163 
164         if (TELEPHONY_COMPONENT_NAME.equals(mComponentName)) {
165             // Telephony phone account handles are now keyed by subscription id which is not
166             // sensitive.
167             sb.append(mId);
168         } else {
169             // Note: Log.pii called for mId as it can contain personally identifying phone account
170             // information such as SIP account IDs.
171             sb.append(Log.pii(mId));
172         }
173         sb.append(", ");
174         sb.append(mUserHandle);
175 
176         return sb.toString();
177     }
178 
179     @Override
equals(Object other)180     public boolean equals(Object other) {
181         return other != null &&
182                 other instanceof PhoneAccountHandle &&
183                 Objects.equals(((PhoneAccountHandle) other).getComponentName(),
184                         getComponentName()) &&
185                 Objects.equals(((PhoneAccountHandle) other).getId(), getId()) &&
186                 Objects.equals(((PhoneAccountHandle) other).getUserHandle(), getUserHandle());
187     }
188 
189     //
190     // Parcelable implementation.
191     //
192 
193     @Override
describeContents()194     public int describeContents() {
195         return 0;
196     }
197 
198     @Override
writeToParcel(Parcel out, int flags)199     public void writeToParcel(Parcel out, int flags) {
200         mComponentName.writeToParcel(out, flags);
201         out.writeString(mId);
202         mUserHandle.writeToParcel(out, flags);
203     }
204 
checkParameters(ComponentName componentName, UserHandle userHandle)205     private void checkParameters(ComponentName componentName, UserHandle userHandle) {
206         if(componentName == null) {
207             android.util.Log.w("PhoneAccountHandle", new Exception("PhoneAccountHandle has " +
208                     "been created with null ComponentName!"));
209         }
210         if(userHandle == null) {
211             android.util.Log.w("PhoneAccountHandle", new Exception("PhoneAccountHandle has " +
212                     "been created with null UserHandle!"));
213         }
214     }
215 
216     public static final @android.annotation.NonNull Creator<PhoneAccountHandle> CREATOR =
217             new Creator<PhoneAccountHandle>() {
218         @Override
219         public PhoneAccountHandle createFromParcel(Parcel in) {
220             return new PhoneAccountHandle(in);
221         }
222 
223         @Override
224         public PhoneAccountHandle[] newArray(int size) {
225             return new PhoneAccountHandle[size];
226         }
227     };
228 
229     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
PhoneAccountHandle(Parcel in)230     private PhoneAccountHandle(Parcel in) {
231         this(ComponentName.CREATOR.createFromParcel(in),
232                 in.readString(),
233                 UserHandle.CREATOR.createFromParcel(in));
234     }
235 
236     /**
237      * Determines if two {@link PhoneAccountHandle}s are from the same package.
238      *
239      * @param a Phone account handle to check for same {@link ConnectionService} package.
240      * @param b Other phone account handle to check for same {@link ConnectionService} package.
241      * @return {@code true} if the two {@link PhoneAccountHandle}s passed in belong to the same
242      * {@link ConnectionService} / package, {@code false} otherwise.  Note: {@code null} phone
243      * account handles are considered equivalent to other {@code null} phone account handles.
244      * @hide
245      */
areFromSamePackage(@ullable PhoneAccountHandle a, @Nullable PhoneAccountHandle b)246     public static boolean areFromSamePackage(@Nullable PhoneAccountHandle a,
247             @Nullable PhoneAccountHandle b) {
248         String aPackageName = a != null ? a.getComponentName().getPackageName() : null;
249         String bPackageName = b != null ? b.getComponentName().getPackageName() : null;
250         return Objects.equals(aPackageName, bPackageName);
251     }
252 }
253