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