Home | History | Annotate | Download | only in telecom
      1 /*
      2  * Copyright (C) 2013 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.SystemApi;
     20 import android.util.ArrayMap;
     21 
     22 import java.util.Collections;
     23 import java.util.List;
     24 import java.util.Map;
     25 import java.util.Objects;
     26 import java.util.concurrent.CopyOnWriteArrayList;
     27 
     28 /**
     29  * A unified virtual device providing a means of voice (and other) communication on a device.
     30  *
     31  * {@hide}
     32  */
     33 @SystemApi
     34 public final class Phone {
     35 
     36     public abstract static class Listener {
     37         /**
     38          * Called when the audio state changes.
     39          *
     40          * @param phone The {@code Phone} calling this method.
     41          * @param audioState The new {@link AudioState}.
     42          */
     43         public void onAudioStateChanged(Phone phone, AudioState audioState) { }
     44 
     45         /**
     46          * Called to bring the in-call screen to the foreground. The in-call experience should
     47          * respond immediately by coming to the foreground to inform the user of the state of
     48          * ongoing {@code Call}s.
     49          *
     50          * @param phone The {@code Phone} calling this method.
     51          * @param showDialpad If true, put up the dialpad when the screen is shown.
     52          */
     53         public void onBringToForeground(Phone phone, boolean showDialpad) { }
     54 
     55         /**
     56          * Called when a {@code Call} has been added to this in-call session. The in-call user
     57          * experience should add necessary state listeners to the specified {@code Call} and
     58          * immediately start to show the user information about the existence
     59          * and nature of this {@code Call}. Subsequent invocations of {@link #getCalls()} will
     60          * include this {@code Call}.
     61          *
     62          * @param phone The {@code Phone} calling this method.
     63          * @param call A newly added {@code Call}.
     64          */
     65         public void onCallAdded(Phone phone, Call call) { }
     66 
     67         /**
     68          * Called when a {@code Call} has been removed from this in-call session. The in-call user
     69          * experience should remove any state listeners from the specified {@code Call} and
     70          * immediately stop displaying any information about this {@code Call}.
     71          * Subsequent invocations of {@link #getCalls()} will no longer include this {@code Call}.
     72          *
     73          * @param phone The {@code Phone} calling this method.
     74          * @param call A newly removed {@code Call}.
     75          */
     76         public void onCallRemoved(Phone phone, Call call) { }
     77 
     78         /**
     79          * Called when the {@code Phone} ability to add more calls changes.  If the phone cannot
     80          * support more calls then {@code canAddCall} is set to {@code false}.  If it can, then it
     81          * is set to {@code true}.
     82          *
     83          * @param phone The {@code Phone} calling this method.
     84          * @param canAddCall Indicates whether an additional call can be added.
     85          */
     86         public void onCanAddCallChanged(Phone phone, boolean canAddCall) { }
     87     }
     88 
     89     // A Map allows us to track each Call by its Telecom-specified call ID
     90     private final Map<String, Call> mCallByTelecomCallId = new ArrayMap<>();
     91 
     92     // A List allows us to keep the Calls in a stable iteration order so that casually developed
     93     // user interface components do not incur any spurious jank
     94     private final List<Call> mCalls = new CopyOnWriteArrayList<>();
     95 
     96     // An unmodifiable view of the above List can be safely shared with subclass implementations
     97     private final List<Call> mUnmodifiableCalls = Collections.unmodifiableList(mCalls);
     98 
     99     private final InCallAdapter mInCallAdapter;
    100 
    101     private AudioState mAudioState;
    102 
    103     private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
    104 
    105     private boolean mCanAddCall = true;
    106 
    107     /** {@hide} */
    108     Phone(InCallAdapter adapter) {
    109         mInCallAdapter = adapter;
    110     }
    111 
    112     /** {@hide} */
    113     final void internalAddCall(ParcelableCall parcelableCall) {
    114         Call call = new Call(this, parcelableCall.getId(), mInCallAdapter);
    115         mCallByTelecomCallId.put(parcelableCall.getId(), call);
    116         mCalls.add(call);
    117         checkCallTree(parcelableCall);
    118         call.internalUpdate(parcelableCall, mCallByTelecomCallId);
    119         fireCallAdded(call);
    120      }
    121 
    122     /** {@hide} */
    123     final void internalRemoveCall(Call call) {
    124         mCallByTelecomCallId.remove(call.internalGetCallId());
    125         mCalls.remove(call);
    126         fireCallRemoved(call);
    127     }
    128 
    129     /** {@hide} */
    130     final void internalUpdateCall(ParcelableCall parcelableCall) {
    131          Call call = mCallByTelecomCallId.get(parcelableCall.getId());
    132          if (call != null) {
    133              checkCallTree(parcelableCall);
    134              call.internalUpdate(parcelableCall, mCallByTelecomCallId);
    135          }
    136      }
    137 
    138     /** {@hide} */
    139     final void internalSetPostDialWait(String telecomId, String remaining) {
    140         Call call = mCallByTelecomCallId.get(telecomId);
    141         if (call != null) {
    142             call.internalSetPostDialWait(remaining);
    143         }
    144     }
    145 
    146     /** {@hide} */
    147     final void internalAudioStateChanged(AudioState audioState) {
    148         if (!Objects.equals(mAudioState, audioState)) {
    149             mAudioState = audioState;
    150             fireAudioStateChanged(audioState);
    151         }
    152     }
    153 
    154     /** {@hide} */
    155     final Call internalGetCallByTelecomId(String telecomId) {
    156         return mCallByTelecomCallId.get(telecomId);
    157     }
    158 
    159     /** {@hide} */
    160     final void internalBringToForeground(boolean showDialpad) {
    161         fireBringToForeground(showDialpad);
    162     }
    163 
    164     /** {@hide} */
    165     final void internalSetCanAddCall(boolean canAddCall) {
    166         if (mCanAddCall != canAddCall) {
    167             mCanAddCall = canAddCall;
    168             fireCanAddCallChanged(canAddCall);
    169         }
    170     }
    171 
    172     /**
    173      * Called to destroy the phone and cleanup any lingering calls.
    174      * @hide
    175      */
    176     final void destroy() {
    177         for (Call call : mCalls) {
    178             if (call.getState() != Call.STATE_DISCONNECTED) {
    179                 call.internalSetDisconnected();
    180             }
    181         }
    182     }
    183 
    184     /**
    185      * Adds a listener to this {@code Phone}.
    186      *
    187      * @param listener A {@code Listener} object.
    188      */
    189     public final void addListener(Listener listener) {
    190         mListeners.add(listener);
    191     }
    192 
    193     /**
    194      * Removes a listener from this {@code Phone}.
    195      *
    196      * @param listener A {@code Listener} object.
    197      */
    198     public final void removeListener(Listener listener) {
    199         if (listener != null) {
    200             mListeners.remove(listener);
    201         }
    202     }
    203 
    204     /**
    205      * Obtains the current list of {@code Call}s to be displayed by this in-call experience.
    206      *
    207      * @return A list of the relevant {@code Call}s.
    208      */
    209     public final List<Call> getCalls() {
    210         return mUnmodifiableCalls;
    211     }
    212 
    213     /**
    214      * Returns if the {@code Phone} can support additional calls.
    215      *
    216      * @return Whether the phone supports adding more calls.
    217      */
    218     public final boolean canAddCall() {
    219         return mCanAddCall;
    220     }
    221 
    222     /**
    223      * Sets the microphone mute state. When this request is honored, there will be change to
    224      * the {@link #getAudioState()}.
    225      *
    226      * @param state {@code true} if the microphone should be muted; {@code false} otherwise.
    227      */
    228     public final void setMuted(boolean state) {
    229         mInCallAdapter.mute(state);
    230     }
    231 
    232     /**
    233      * Sets the audio route (speaker, bluetooth, etc...).  When this request is honored, there will
    234      * be change to the {@link #getAudioState()}.
    235      *
    236      * @param route The audio route to use.
    237      */
    238     public final void setAudioRoute(int route) {
    239         mInCallAdapter.setAudioRoute(route);
    240     }
    241 
    242     /**
    243      * Turns the proximity sensor on. When this request is made, the proximity sensor will
    244      * become active, and the touch screen and display will be turned off when the user's face
    245      * is detected to be in close proximity to the screen. This operation is a no-op on devices
    246      * that do not have a proximity sensor.
    247      */
    248     public final void setProximitySensorOn() {
    249         mInCallAdapter.turnProximitySensorOn();
    250     }
    251 
    252     /**
    253      * Turns the proximity sensor off. When this request is made, the proximity sensor will
    254      * become inactive, and no longer affect the touch screen and display. This operation is a
    255      * no-op on devices that do not have a proximity sensor.
    256      *
    257      * @param screenOnImmediately If true, the screen will be turned on immediately if it was
    258      * previously off. Otherwise, the screen will only be turned on after the proximity sensor
    259      * is no longer triggered.
    260      */
    261     public final void setProximitySensorOff(boolean screenOnImmediately) {
    262         mInCallAdapter.turnProximitySensorOff(screenOnImmediately);
    263     }
    264 
    265     /**
    266      * Obtains the current phone call audio state of the {@code Phone}.
    267      *
    268      * @return An object encapsulating the audio state.
    269      */
    270     public final AudioState getAudioState() {
    271         return mAudioState;
    272     }
    273 
    274     private void fireCallAdded(Call call) {
    275         for (Listener listener : mListeners) {
    276             listener.onCallAdded(this, call);
    277         }
    278     }
    279 
    280     private void fireCallRemoved(Call call) {
    281         for (Listener listener : mListeners) {
    282             listener.onCallRemoved(this, call);
    283         }
    284     }
    285 
    286     private void fireAudioStateChanged(AudioState audioState) {
    287         for (Listener listener : mListeners) {
    288             listener.onAudioStateChanged(this, audioState);
    289         }
    290     }
    291 
    292     private void fireBringToForeground(boolean showDialpad) {
    293         for (Listener listener : mListeners) {
    294             listener.onBringToForeground(this, showDialpad);
    295         }
    296     }
    297 
    298     private void fireCanAddCallChanged(boolean canAddCall) {
    299         for (Listener listener : mListeners) {
    300             listener.onCanAddCallChanged(this, canAddCall);
    301         }
    302     }
    303 
    304     private void checkCallTree(ParcelableCall parcelableCall) {
    305         if (parcelableCall.getParentCallId() != null &&
    306                 !mCallByTelecomCallId.containsKey(parcelableCall.getParentCallId())) {
    307             Log.wtf(this, "ParcelableCall %s has nonexistent parent %s",
    308                     parcelableCall.getId(), parcelableCall.getParentCallId());
    309         }
    310         if (parcelableCall.getChildCallIds() != null) {
    311             for (int i = 0; i < parcelableCall.getChildCallIds().size(); i++) {
    312                 if (!mCallByTelecomCallId.containsKey(parcelableCall.getChildCallIds().get(i))) {
    313                     Log.wtf(this, "ParcelableCall %s has nonexistent child %s",
    314                             parcelableCall.getId(), parcelableCall.getChildCallIds().get(i));
    315                 }
    316             }
    317         }
    318     }
    319 }
    320