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