1 /*
2  * Copyright (C) 2018 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.telephony.mbms;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.os.RemoteException;
22 import android.telephony.MbmsGroupCallSession;
23 import android.telephony.mbms.vendor.IMbmsGroupCallService;
24 import android.util.Log;
25 
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.util.List;
29 
30 /**
31  * Class used to represent a single MBMS group call. After a call has been started with
32  * {@link MbmsGroupCallSession#startGroupCall},
33  * this class is used to hold information about the call and control it.
34  */
35 public class GroupCall implements AutoCloseable {
36     private static final String LOG_TAG = "MbmsGroupCall";
37 
38     /**
39      * The state of a group call, reported via
40      * {@link GroupCallCallback#onGroupCallStateChanged(int, int)}
41      * @hide
42      */
43     @Retention(RetentionPolicy.SOURCE)
44     @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
45     public @interface GroupCallState {}
46 
47     /**
48      * Indicates that the group call is in a stopped state
49      *
50      * This can be reported after network action or after calling {@link #close}.
51      */
52     public static final int STATE_STOPPED = 1;
53 
54     /**
55      * Indicates that the group call is started.
56      *
57      * Data can be transmitted and received in this state.
58      */
59     public static final int STATE_STARTED = 2;
60 
61     /**
62      * Indicates that the group call is stalled.
63      *
64      * This may be due to a network issue or the device being temporarily out of range.
65      */
66     public static final int STATE_STALLED = 3;
67 
68     /**
69      * The reason for a call state change, reported via
70      * {@link GroupCallCallback#onGroupCallStateChanged(int, int)}
71      * @hide
72      */
73     @Retention(RetentionPolicy.SOURCE)
74     @IntDef(prefix = { "REASON_" },
75             value = {REASON_BY_USER_REQUEST, REASON_FREQUENCY_CONFLICT,
76                     REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
77                     REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
78     public @interface GroupCallStateChangeReason {}
79 
80     /**
81      * Indicates that the middleware does not have a reason to provide for the state change.
82      */
83     public static final int REASON_NONE = 0;
84 
85     /**
86      * State changed due to a call to {@link #close()} or
87      * {@link MbmsGroupCallSession#startGroupCall}
88      */
89     public static final int REASON_BY_USER_REQUEST = 1;
90 
91     // 2 is unused to match up with streaming.
92 
93     /**
94      * State changed due to a frequency conflict with another requested call.
95      */
96     public static final int REASON_FREQUENCY_CONFLICT = 3;
97 
98     /**
99      * State changed due to the middleware running out of memory
100      */
101     public static final int REASON_OUT_OF_MEMORY = 4;
102 
103     /**
104      * State changed due to the device leaving the home carrier's LTE network.
105      */
106     public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5;
107 
108     /**
109      * State changed due to the device leaving the area where this call is being broadcast.
110      */
111     public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6;
112 
113     private final int mSubscriptionId;
114     private final long mTmgi;
115     private final MbmsGroupCallSession mParentSession;
116     private final InternalGroupCallCallback mCallback;
117     private IMbmsGroupCallService mService;
118 
119     /**
120      * @hide
121      */
GroupCall(int subscriptionId, IMbmsGroupCallService service, MbmsGroupCallSession session, long tmgi, InternalGroupCallCallback callback)122     public GroupCall(int subscriptionId,
123             IMbmsGroupCallService service,
124             MbmsGroupCallSession session,
125             long tmgi,
126             InternalGroupCallCallback callback) {
127         mSubscriptionId = subscriptionId;
128         mParentSession = session;
129         mService = service;
130         mTmgi = tmgi;
131         mCallback = callback;
132     }
133 
134     /**
135      * Retrieve the TMGI (Temporary Mobile Group Identity) corresponding to this call.
136      */
getTmgi()137     public long getTmgi() {
138         return mTmgi;
139     }
140 
141     /**
142      * Send an update to the middleware when the SAI (Service Area Identifier) list and frequency
143      * information of the group call has * changed. Callers must obtain this information from the
144      * wireless carrier independently.
145      * @param saiList New list of SAIs that the call is available on.
146      * @param frequencyList New list of frequencies that the call is available on.
147      */
updateGroupCall(@onNull List<Integer> saiList, @NonNull List<Integer> frequencyList)148     public void updateGroupCall(@NonNull List<Integer> saiList,
149             @NonNull List<Integer> frequencyList) {
150         if (mService == null) {
151             throw new IllegalStateException("No group call service attached");
152         }
153 
154         try {
155             mService.updateGroupCall(mSubscriptionId, mTmgi, saiList, frequencyList);
156         } catch (RemoteException e) {
157             Log.w(LOG_TAG, "Remote process died");
158             mService = null;
159             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
160         } finally {
161             mParentSession.onGroupCallStopped(this);
162         }
163     }
164 
165     /**
166      * Stop this group call. Further operations on this object will fail with an
167      * {@link IllegalStateException}.
168      *
169      * May throw an {@link IllegalStateException}
170      */
171     @Override
close()172     public void close() {
173         if (mService == null) {
174             throw new IllegalStateException("No group call service attached");
175         }
176 
177         try {
178             mService.stopGroupCall(mSubscriptionId, mTmgi);
179         } catch (RemoteException e) {
180             Log.w(LOG_TAG, "Remote process died");
181             mService = null;
182             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
183         } finally {
184             mParentSession.onGroupCallStopped(this);
185         }
186     }
187 
188     /** @hide */
getCallback()189     public InternalGroupCallCallback getCallback() {
190         return mCallback;
191     }
192 
sendErrorToApp(int errorCode, String message)193     private void sendErrorToApp(int errorCode, String message) {
194         mCallback.onError(errorCode, message);
195     }
196 }
197 
198