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