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.SystemApi;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.pm.PackageManager;
23import android.content.res.Resources.NotFoundException;
24import android.graphics.Bitmap;
25import android.graphics.Color;
26import android.graphics.drawable.BitmapDrawable;
27import android.graphics.drawable.ColorDrawable;
28import android.graphics.drawable.Drawable;
29import android.net.Uri;
30import android.os.Parcel;
31import android.os.Parcelable;
32import android.text.TextUtils;
33
34import java.lang.String;
35import java.util.ArrayList;
36import java.util.Collections;
37import java.util.List;
38import java.util.MissingResourceException;
39
40/**
41 * Represents a distinct method to place or receive a phone call. Apps which can place calls and
42 * want those calls to be integrated into the dialer and in-call UI should build an instance of
43 * this class and register it with the system using {@link TelecomManager#registerPhoneAccount}.
44 * <p>
45 * {@link TelecomManager} uses registered {@link PhoneAccount}s to present the user with
46 * alternative options when placing a phone call. When building a {@link PhoneAccount}, the app
47 * should supply a valid {@link PhoneAccountHandle} that references the {@link ConnectionService}
48 * implementation Telecom will use to interact with the app.
49 * @hide
50 */
51@SystemApi
52public class PhoneAccount implements Parcelable {
53
54    /**
55     * Flag indicating that this {@code PhoneAccount} can act as a connection manager for
56     * other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
57     * will be allowed to manage phone calls including using its own proprietary phone-call
58     * implementation (like VoIP calling) to make calls instead of the telephony stack.
59     * <p>
60     * When a user opts to place a call using the SIM-based telephony stack, the
61     * {@link ConnectionService} associated with this {@code PhoneAccount} will be attempted first
62     * if the user has explicitly selected it to be used as the default connection manager.
63     * <p>
64     * See {@link #getCapabilities}
65     */
66    public static final int CAPABILITY_CONNECTION_MANAGER = 0x1;
67
68    /**
69     * Flag indicating that this {@code PhoneAccount} can make phone calls in place of
70     * traditional SIM-based telephony calls. This account will be treated as a distinct method
71     * for placing calls alongside the traditional SIM-based telephony stack. This flag is
72     * distinct from {@link #CAPABILITY_CONNECTION_MANAGER} in that it is not allowed to manage
73     * or place calls from the built-in telephony stack.
74     * <p>
75     * See {@link #getCapabilities}
76     * <p>
77     * {@hide}
78     */
79    public static final int CAPABILITY_CALL_PROVIDER = 0x2;
80
81    /**
82     * Flag indicating that this {@code PhoneAccount} represents a built-in PSTN SIM
83     * subscription.
84     * <p>
85     * Only the Android framework can register a {@code PhoneAccount} having this capability.
86     * <p>
87     * See {@link #getCapabilities}
88     */
89    public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4;
90
91    /**
92     * Flag indicating that this {@code PhoneAccount} is capable of placing video calls.
93     * <p>
94     * See {@link #getCapabilities}
95     * @hide
96     */
97    public static final int CAPABILITY_VIDEO_CALLING = 0x8;
98
99    /**
100     * Flag indicating that this {@code PhoneAccount} is capable of placing emergency calls.
101     * By default all PSTN {@code PhoneAccount}s are capable of placing emergency calls.
102     * <p>
103     * See {@link #getCapabilities}
104     */
105    public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 0x10;
106
107    /**
108     * Flag indicating that this {@code PhoneAccount} is capable of being used by all users. This
109     * should only be used by system apps (and will be ignored for all other apps trying to use it).
110     * <p>
111     * See {@link #getCapabilities}
112     * @hide
113     */
114    public static final int CAPABILITY_MULTI_USER = 0x20;
115
116    /**
117     * URI scheme for telephone number URIs.
118     */
119    public static final String SCHEME_TEL = "tel";
120
121    /**
122     * URI scheme for voicemail URIs.
123     */
124    public static final String SCHEME_VOICEMAIL = "voicemail";
125
126    /**
127     * URI scheme for SIP URIs.
128     */
129    public static final String SCHEME_SIP = "sip";
130
131    /**
132     * Indicating no icon tint is set.
133     */
134    public static final int NO_ICON_TINT = 0;
135
136    /**
137     * Indicating no hightlight color is set.
138     */
139    public static final int NO_HIGHLIGHT_COLOR = 0;
140
141    /**
142     * Indicating no resource ID is set.
143     */
144    public static final int NO_RESOURCE_ID = -1;
145
146    private final PhoneAccountHandle mAccountHandle;
147    private final Uri mAddress;
148    private final Uri mSubscriptionAddress;
149    private final int mCapabilities;
150    private final int mIconResId;
151    private final String mIconPackageName;
152    private final Bitmap mIconBitmap;
153    private final int mIconTint;
154    private final int mHighlightColor;
155    private final CharSequence mLabel;
156    private final CharSequence mShortDescription;
157    private final List<String> mSupportedUriSchemes;
158
159    /**
160     * Helper class for creating a {@link PhoneAccount}.
161     */
162    public static class Builder {
163        private PhoneAccountHandle mAccountHandle;
164        private Uri mAddress;
165        private Uri mSubscriptionAddress;
166        private int mCapabilities;
167        private int mIconResId;
168        private String mIconPackageName;
169        private Bitmap mIconBitmap;
170        private int mIconTint = NO_ICON_TINT;
171        private int mHighlightColor = NO_HIGHLIGHT_COLOR;
172        private CharSequence mLabel;
173        private CharSequence mShortDescription;
174        private List<String> mSupportedUriSchemes = new ArrayList<String>();
175
176        /**
177         * Creates a builder with the specified {@link PhoneAccountHandle} and label.
178         */
179        public Builder(PhoneAccountHandle accountHandle, CharSequence label) {
180            this.mAccountHandle = accountHandle;
181            this.mLabel = label;
182        }
183
184        /**
185         * Creates an instance of the {@link PhoneAccount.Builder} from an existing
186         * {@link PhoneAccount}.
187         *
188         * @param phoneAccount The {@link PhoneAccount} used to initialize the builder.
189         */
190        public Builder(PhoneAccount phoneAccount) {
191            mAccountHandle = phoneAccount.getAccountHandle();
192            mAddress = phoneAccount.getAddress();
193            mSubscriptionAddress = phoneAccount.getSubscriptionAddress();
194            mCapabilities = phoneAccount.getCapabilities();
195            mIconResId = phoneAccount.getIconResId();
196            mIconPackageName = phoneAccount.getIconPackageName();
197            mIconBitmap = phoneAccount.getIconBitmap();
198            mIconTint = phoneAccount.getIconTint();
199            mHighlightColor = phoneAccount.getHighlightColor();
200            mLabel = phoneAccount.getLabel();
201            mShortDescription = phoneAccount.getShortDescription();
202            mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
203        }
204
205        /** @hide */
206        public Builder setAccountHandle(PhoneAccountHandle accountHandle) {
207            mAccountHandle = accountHandle;
208            return this;
209        }
210
211        /**
212         * Sets the address. See {@link PhoneAccount#getAddress}.
213         *
214         * @param value The address of the phone account.
215         * @return The builder.
216         */
217        public Builder setAddress(Uri value) {
218            this.mAddress = value;
219            return this;
220        }
221
222        /**
223         * Sets the subscription address. See {@link PhoneAccount#getSubscriptionAddress}.
224         *
225         * @param value The subscription address.
226         * @return The builder.
227         */
228        public Builder setSubscriptionAddress(Uri value) {
229            this.mSubscriptionAddress = value;
230            return this;
231        }
232
233        /**
234         * Sets the capabilities. See {@link PhoneAccount#getCapabilities}.
235         *
236         * @param value The capabilities to set.
237         * @return The builder.
238         */
239        public Builder setCapabilities(int value) {
240            this.mCapabilities = value;
241            return this;
242        }
243
244        /**
245         * Sets the icon. See {@link PhoneAccount#createIconDrawable}.
246         *
247         * @param packageContext The package from which to load an icon.
248         * @param iconResId The resource in {@code iconPackageName} representing the icon.
249         * @return The builder.
250         */
251        public Builder setIcon(Context packageContext, int iconResId) {
252            return setIcon(packageContext.getPackageName(), iconResId);
253        }
254
255        /**
256         * Sets the icon. See {@link PhoneAccount#createIconDrawable}.
257         *
258         * @param iconPackageName The package from which to load an icon.
259         * @param iconResId The resource in {@code iconPackageName} representing the icon.
260         * @return The builder.
261         */
262        public Builder setIcon(String iconPackageName, int iconResId) {
263            return setIcon(iconPackageName, iconResId, NO_ICON_TINT);
264        }
265
266        /**
267         * Sets the icon. See {@link PhoneAccount#createIconDrawable}.
268         *
269         * @param packageContext The package from which to load an icon.
270         * @param iconResId The resource in {@code iconPackageName} representing the icon.
271         * @param iconTint A color with which to tint this icon.
272         * @return The builder.
273         */
274        public Builder setIcon(Context packageContext, int iconResId, int iconTint) {
275            return setIcon(packageContext.getPackageName(), iconResId, iconTint);
276        }
277
278        /**
279         * Sets the icon. See {@link PhoneAccount#createIconDrawable}.
280         *
281         * @param iconPackageName The package from which to load an icon.
282         * @param iconResId The resource in {@code iconPackageName} representing the icon.
283         * @param iconTint A color with which to tint this icon.
284         * @return The builder.
285         */
286        public Builder setIcon(String iconPackageName, int iconResId, int iconTint) {
287            this.mIconPackageName = iconPackageName;
288            this.mIconResId = iconResId;
289            this.mIconTint = iconTint;
290            return this;
291        }
292
293        /**
294         * Sets the icon. See {@link PhoneAccount#createIconDrawable}.
295         *
296         * @param iconBitmap The icon bitmap.
297         * @return The builder.
298         */
299        public Builder setIcon(Bitmap iconBitmap) {
300            this.mIconBitmap = iconBitmap;
301            this.mIconPackageName = null;
302            this.mIconResId = NO_RESOURCE_ID;
303            this.mIconTint = NO_ICON_TINT;
304            return this;
305        }
306
307        /**
308         * Sets the highlight color. See {@link PhoneAccount#getHighlightColor}.
309         *
310         * @param value The highlight color.
311         * @return The builder.
312         */
313        public Builder setHighlightColor(int value) {
314            this.mHighlightColor = value;
315            return this;
316        }
317
318        /**
319         * Sets the short description. See {@link PhoneAccount#getShortDescription}.
320         *
321         * @param value The short description.
322         * @return The builder.
323         */
324        public Builder setShortDescription(CharSequence value) {
325            this.mShortDescription = value;
326            return this;
327        }
328
329        /**
330         * Specifies an additional URI scheme supported by the {@link PhoneAccount}.
331         *
332         * @param uriScheme The URI scheme.
333         * @return The builder.
334         * @hide
335         */
336        public Builder addSupportedUriScheme(String uriScheme) {
337            if (!TextUtils.isEmpty(uriScheme) && !mSupportedUriSchemes.contains(uriScheme)) {
338                this.mSupportedUriSchemes.add(uriScheme);
339            }
340            return this;
341        }
342
343        /**
344         * Specifies the URI schemes supported by the {@link PhoneAccount}.
345         *
346         * @param uriSchemes The URI schemes.
347         * @return The builder.
348         */
349        public Builder setSupportedUriSchemes(List<String> uriSchemes) {
350            mSupportedUriSchemes.clear();
351
352            if (uriSchemes != null && !uriSchemes.isEmpty()) {
353                for (String uriScheme : uriSchemes) {
354                    addSupportedUriScheme(uriScheme);
355                }
356            }
357            return this;
358        }
359
360        /**
361         * Creates an instance of a {@link PhoneAccount} based on the current builder settings.
362         *
363         * @return The {@link PhoneAccount}.
364         */
365        public PhoneAccount build() {
366            // If no supported URI schemes were defined, assume "tel" is supported.
367            if (mSupportedUriSchemes.isEmpty()) {
368                addSupportedUriScheme(SCHEME_TEL);
369            }
370
371            return new PhoneAccount(
372                    mAccountHandle,
373                    mAddress,
374                    mSubscriptionAddress,
375                    mCapabilities,
376                    mIconResId,
377                    mIconPackageName,
378                    mIconBitmap,
379                    mIconTint,
380                    mHighlightColor,
381                    mLabel,
382                    mShortDescription,
383                    mSupportedUriSchemes);
384        }
385    }
386
387    private PhoneAccount(
388            PhoneAccountHandle account,
389            Uri address,
390            Uri subscriptionAddress,
391            int capabilities,
392            int iconResId,
393            String iconPackageName,
394            Bitmap iconBitmap,
395            int iconTint,
396            int highlightColor,
397            CharSequence label,
398            CharSequence shortDescription,
399            List<String> supportedUriSchemes) {
400        mAccountHandle = account;
401        mAddress = address;
402        mSubscriptionAddress = subscriptionAddress;
403        mCapabilities = capabilities;
404        mIconResId = iconResId;
405        mIconPackageName = iconPackageName;
406        mIconBitmap = iconBitmap;
407        mIconTint = iconTint;
408        mHighlightColor = highlightColor;
409        mLabel = label;
410        mShortDescription = shortDescription;
411        mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
412    }
413
414    public static Builder builder(
415            PhoneAccountHandle accountHandle,
416            CharSequence label) {
417        return new Builder(accountHandle, label);
418    }
419
420    /**
421     * Returns a builder initialized with the current {@link PhoneAccount} instance.
422     *
423     * @return The builder.
424     * @hide
425     */
426    public Builder toBuilder() { return new Builder(this); }
427
428    /**
429     * The unique identifier of this {@code PhoneAccount}.
430     *
431     * @return A {@code PhoneAccountHandle}.
432     */
433    public PhoneAccountHandle getAccountHandle() {
434        return mAccountHandle;
435    }
436
437    /**
438     * The address (e.g., a phone number) associated with this {@code PhoneAccount}. This
439     * represents the destination from which outgoing calls using this {@code PhoneAccount}
440     * will appear to come, if applicable, and the destination to which incoming calls using this
441     * {@code PhoneAccount} may be addressed.
442     *
443     * @return A address expressed as a {@code Uri}, for example, a phone number.
444     */
445    public Uri getAddress() {
446        return mAddress;
447    }
448
449    /**
450     * The raw callback number used for this {@code PhoneAccount}, as distinct from
451     * {@link #getAddress()}. For the majority of {@code PhoneAccount}s this should be registered
452     * as {@code null}.  It is used by the system for SIM-based {@code PhoneAccount} registration
453     * where {@link android.telephony.TelephonyManager#setLine1NumberForDisplay(String, String)}
454     * has been used to alter the callback number.
455     * <p>
456     *
457     * @return The subscription number, suitable for display to the user.
458     */
459    public Uri getSubscriptionAddress() {
460        return mSubscriptionAddress;
461    }
462
463    /**
464     * The capabilities of this {@code PhoneAccount}.
465     *
466     * @return A bit field of flags describing this {@code PhoneAccount}'s capabilities.
467     */
468    public int getCapabilities() {
469        return mCapabilities;
470    }
471
472    /**
473     * Determines if this {@code PhoneAccount} has a capabilities specified by the passed in
474     * bit mask.
475     *
476     * @param capability The capabilities to check.
477     * @return {@code True} if the phone account has the capability.
478     */
479    public boolean hasCapabilities(int capability) {
480        return (mCapabilities & capability) == capability;
481    }
482
483    /**
484     * A short label describing a {@code PhoneAccount}.
485     *
486     * @return A label for this {@code PhoneAccount}.
487     */
488    public CharSequence getLabel() {
489        return mLabel;
490    }
491
492    /**
493     * A short paragraph describing this {@code PhoneAccount}.
494     *
495     * @return A description for this {@code PhoneAccount}.
496     */
497    public CharSequence getShortDescription() {
498        return mShortDescription;
499    }
500
501    /**
502     * The URI schemes supported by this {@code PhoneAccount}.
503     *
504     * @return The URI schemes.
505     */
506    public List<String> getSupportedUriSchemes() {
507        return mSupportedUriSchemes;
508    }
509
510    /**
511     * Determines if the {@link PhoneAccount} supports calls to/from addresses with a specified URI
512     * scheme.
513     *
514     * @param uriScheme The URI scheme to check.
515     * @return {@code True} if the {@code PhoneAccount} supports calls to/from addresses with the
516     * specified URI scheme.
517     */
518    public boolean supportsUriScheme(String uriScheme) {
519        if (mSupportedUriSchemes == null || uriScheme == null) {
520            return false;
521        }
522
523        for (String scheme : mSupportedUriSchemes) {
524            if (scheme != null && scheme.equals(uriScheme)) {
525                return true;
526            }
527        }
528        return false;
529    }
530
531    /**
532     * The icon resource ID for the icon of this {@code PhoneAccount}.
533     * <p>
534     * Creators of a {@code PhoneAccount} who possess the icon in static resources should prefer
535     * this method of indicating the icon rather than using {@link #getIconBitmap()}, since it
536     * leads to less resource usage.
537     * <p>
538     * Clients wishing to display a {@code PhoneAccount} should use {@link #createIconDrawable(Context)}.
539     *
540     * @return A resource ID.
541     */
542    public int getIconResId() {
543        return mIconResId;
544    }
545
546    /**
547     * The package name from which to load the icon of this {@code PhoneAccount}.
548     * <p>
549     * If this property is {@code null}, the resource {@link #getIconResId()} will be loaded from
550     * the package in the {@link ComponentName} of the {@link #getAccountHandle()}.
551     * <p>
552     * Clients wishing to display a {@code PhoneAccount} should use {@link #createIconDrawable(Context)}.
553     *
554     * @return A package name.
555     */
556    public String getIconPackageName() {
557        return mIconPackageName;
558    }
559
560    /**
561     * A tint to apply to the icon of this {@code PhoneAccount}.
562     *
563     * @return A hexadecimal color value.
564     */
565    public int getIconTint() {
566        return mIconTint;
567    }
568
569    /**
570     * A literal icon bitmap to represent this {@code PhoneAccount} in a user interface.
571     * <p>
572     * If this property is specified, it is to be considered the preferred icon. Otherwise, the
573     * resource specified by {@link #getIconResId()} should be used.
574     * <p>
575     * Clients wishing to display a {@code PhoneAccount} should use
576     * {@link #createIconDrawable(Context)}.
577     *
578     * @return A bitmap.
579     */
580    public Bitmap getIconBitmap() {
581        return mIconBitmap;
582    }
583
584    /**
585     * A highlight color to use in displaying information about this {@code PhoneAccount}.
586     *
587     * @return A hexadecimal color value.
588     */
589    public int getHighlightColor() {
590        return mHighlightColor;
591    }
592
593    /**
594     * Builds and returns an icon {@code Drawable} to represent this {@code PhoneAccount} in a user
595     * interface. Uses the properties {@link #getIconResId()}, {@link #getIconPackageName()}, and
596     * {@link #getIconBitmap()} as necessary.
597     *
598     * @param context A {@code Context} to use for loading {@code Drawable}s.
599     *
600     * @return An icon for this {@code PhoneAccount}.
601     */
602    public Drawable createIconDrawable(Context context) {
603        if (mIconBitmap != null) {
604            return new BitmapDrawable(context.getResources(), mIconBitmap);
605        }
606
607        if (mIconResId != 0) {
608            try {
609                Context packageContext = context.createPackageContext(mIconPackageName, 0);
610                try {
611                    Drawable iconDrawable = packageContext.getDrawable(mIconResId);
612                    if (mIconTint != NO_ICON_TINT) {
613                        iconDrawable.setTint(mIconTint);
614                    }
615                    return iconDrawable;
616                } catch (NotFoundException | MissingResourceException e) {
617                    Log.e(this, e, "Cannot find icon %d in package %s",
618                            mIconResId, mIconPackageName);
619                }
620            } catch (PackageManager.NameNotFoundException e) {
621                Log.w(this, "Cannot find package %s", mIconPackageName);
622            }
623        }
624
625        return new ColorDrawable(Color.TRANSPARENT);
626    }
627
628    //
629    // Parcelable implementation
630    //
631
632    @Override
633    public int describeContents() {
634        return 0;
635    }
636
637    @Override
638    public void writeToParcel(Parcel out, int flags) {
639        if (mAccountHandle == null) {
640            out.writeInt(0);
641        } else {
642            out.writeInt(1);
643            mAccountHandle.writeToParcel(out, flags);
644        }
645        if (mAddress == null) {
646            out.writeInt(0);
647        } else {
648            out.writeInt(1);
649            mAddress.writeToParcel(out, flags);
650        }
651        if (mSubscriptionAddress == null) {
652            out.writeInt(0);
653        } else {
654            out.writeInt(1);
655            mSubscriptionAddress.writeToParcel(out, flags);
656        }
657        out.writeInt(mCapabilities);
658        out.writeInt(mIconResId);
659        out.writeString(mIconPackageName);
660        if (mIconBitmap == null) {
661            out.writeInt(0);
662        } else {
663            out.writeInt(1);
664            mIconBitmap.writeToParcel(out, flags);
665        }
666        out.writeInt(mIconTint);
667        out.writeInt(mHighlightColor);
668        out.writeCharSequence(mLabel);
669        out.writeCharSequence(mShortDescription);
670        out.writeStringList(mSupportedUriSchemes);
671    }
672
673    public static final Creator<PhoneAccount> CREATOR
674            = new Creator<PhoneAccount>() {
675        @Override
676        public PhoneAccount createFromParcel(Parcel in) {
677            return new PhoneAccount(in);
678        }
679
680        @Override
681        public PhoneAccount[] newArray(int size) {
682            return new PhoneAccount[size];
683        }
684    };
685
686    private PhoneAccount(Parcel in) {
687        if (in.readInt() > 0) {
688            mAccountHandle = PhoneAccountHandle.CREATOR.createFromParcel(in);
689        } else {
690            mAccountHandle = null;
691        }
692        if (in.readInt() > 0) {
693            mAddress = Uri.CREATOR.createFromParcel(in);
694        } else {
695            mAddress = null;
696        }
697        if (in.readInt() > 0) {
698            mSubscriptionAddress = Uri.CREATOR.createFromParcel(in);
699        } else {
700            mSubscriptionAddress = null;
701        }
702        mCapabilities = in.readInt();
703        mIconResId = in.readInt();
704        mIconPackageName = in.readString();
705        if (in.readInt() > 0) {
706            mIconBitmap = Bitmap.CREATOR.createFromParcel(in);
707        } else {
708            mIconBitmap = null;
709        }
710        mIconTint = in.readInt();
711        mHighlightColor = in.readInt();
712        mLabel = in.readCharSequence();
713        mShortDescription = in.readCharSequence();
714        mSupportedUriSchemes = Collections.unmodifiableList(in.createStringArrayList());
715    }
716
717    @Override
718    public String toString() {
719        StringBuilder sb = new StringBuilder().append("[PhoneAccount: ")
720                .append(mAccountHandle)
721                .append(" Capabilities: ")
722                .append(mCapabilities)
723                .append(" Schemes: ");
724        for (String scheme : mSupportedUriSchemes) {
725            sb.append(scheme)
726                    .append(" ");
727        }
728        sb.append("]");
729        return sb.toString();
730    }
731}
732