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 com.android.server.telecom.testapps;
18 
19 import com.android.server.telecom.testapps.R;
20 
21 import android.app.Notification;
22 import android.app.NotificationChannel;
23 import android.app.NotificationManager;
24 import android.app.PendingIntent;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.graphics.Color;
29 import android.graphics.drawable.Icon;
30 import android.media.AudioAttributes;
31 import android.net.Uri;
32 import android.os.Bundle;
33 import android.telecom.PhoneAccount;
34 import android.telecom.PhoneAccountHandle;
35 import android.telecom.TelecomManager;
36 import android.util.Log;
37 import android.widget.Toast;
38 
39 import java.util.Arrays;
40 import java.util.HashMap;
41 import java.util.List;
42 import java.util.Map;
43 
44 /**
45  * Class used to create, update and cancel the notification used to display and update call state
46  * for {@link TestConnectionService}.
47  */
48 public class CallServiceNotifier {
49     private static final CallServiceNotifier INSTANCE = new CallServiceNotifier();
50     private static final String CHANNEL_ID = "channel1";
51 
52     public static final String CALL_PROVIDER_ID = "testapps_TestConnectionService_CALL_PROVIDER_ID";
53     public static final String SIM_SUBSCRIPTION_ID =
54             "testapps_TestConnectionService_SIM_SUBSCRIPTION_ID";
55     public static final String SIM_SUBSCRIPTION_ID2 =
56             "testapps_TestConnectionService_SIM_SUBSCRIPTION_ID2";
57     public static final String CONNECTION_MANAGER_ID =
58             "testapps_TestConnectionService_CONNECTION_MANAGER_ID";
59 
60     private Map<String, PhoneAccountHandle> mPhoneAccountMap = new HashMap<>();
61 
62     /**
63      * Static notification IDs.
64      */
65     private static final int CALL_NOTIFICATION_ID = 1;
66     private static final int PHONE_ACCOUNT_NOTIFICATION_ID = 2;
67 
68     /**
69      * Whether the added call should be started as a video call. Referenced by
70      * {@link TestConnectionService} to know whether to provide a call video provider.
71      */
72     public static boolean mStartVideoCall;
73 
74     /**
75      * Singleton accessor.
76      */
getInstance()77     public static CallServiceNotifier getInstance() {
78         return INSTANCE;
79     }
80 
81     /**
82      * Creates a CallService & initializes notification manager.
83      */
CallServiceNotifier()84     private CallServiceNotifier() {
85     }
86 
87     /**
88      * Updates the notification in the notification pane.
89      */
updateNotification(Context context)90     public void updateNotification(Context context) {
91         log("adding the notification ------------");
92         NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Test Channel",
93                 NotificationManager.IMPORTANCE_DEFAULT);
94         getNotificationManager(context).createNotificationChannel(channel);
95         getNotificationManager(context).notify(CALL_NOTIFICATION_ID, getMainNotification(context));
96         getNotificationManager(context).notify(
97                 PHONE_ACCOUNT_NOTIFICATION_ID, getPhoneAccountNotification(context));
98     }
99 
100     /**
101      * Cancels the notification.
102      */
cancelNotifications(Context context)103     public void cancelNotifications(Context context) {
104         log("canceling notification");
105         getNotificationManager(context).cancel(CALL_NOTIFICATION_ID);
106         getNotificationManager(context).cancel(PHONE_ACCOUNT_NOTIFICATION_ID);
107     }
108 
109     /**
110      * Registers a phone account with telecom.
111      */
registerPhoneAccount(Context context)112     public void registerPhoneAccount(Context context) {
113         TelecomManager telecomManager =
114                 (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
115 
116         telecomManager.clearAccounts();
117 
118         Bundle testBundle = new Bundle();
119         testBundle.putInt("EXTRA_INT_1", 1);
120         testBundle.putInt("EXTRA_INT_100", 100);
121         testBundle.putBoolean("EXTRA_BOOL_TRUE", true);
122         testBundle.putBoolean("EXTRA_BOOL_FALSE", false);
123         testBundle.putString("EXTRA_STR1", "Hello");
124         testBundle.putString("EXTRA_STR2", "There");
125 
126         PhoneAccountHandle handle1 = new PhoneAccountHandle(
127                 new ComponentName(context, TestConnectionService.class), CALL_PROVIDER_ID);
128         mPhoneAccountMap.put(CALL_PROVIDER_ID, handle1);
129         telecomManager.registerPhoneAccount(PhoneAccount.builder(
130                 handle1,
131                 "TelecomTestApp Call Provider")
132                 .setAddress(Uri.parse("tel:555-TEST"))
133                 .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
134                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
135                         PhoneAccount.CAPABILITY_VIDEO_CALLING |
136                         PhoneAccount.CAPABILITY_RTT |
137                         PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)
138                 .setIcon(Icon.createWithResource(
139                         context.getResources(), R.drawable.stat_sys_phone_call))
140                 .setHighlightColor(Color.RED)
141                 // TODO: Add icon tint (Color.RED)
142                 .setShortDescription("a short description for the call provider")
143                 .setSupportedUriSchemes(Arrays.asList("tel"))
144                 .setExtras(testBundle)
145                 .build());
146 
147         PhoneAccountHandle handle2 = new PhoneAccountHandle(
148                 new ComponentName(context, TestConnectionService.class), SIM_SUBSCRIPTION_ID);
149         mPhoneAccountMap.put(SIM_SUBSCRIPTION_ID, handle2);
150         telecomManager.registerPhoneAccount(PhoneAccount.builder(
151                 handle2,
152                 "TelecomTestApp SIM Subscription")
153                 .setAddress(Uri.parse("tel:555-TSIM"))
154                 .setSubscriptionAddress(Uri.parse("tel:555-TSIM"))
155                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
156                         PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
157                         PhoneAccount.CAPABILITY_VIDEO_CALLING |
158                         PhoneAccount.CAPABILITY_RTT |
159                         PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)
160                 .setIcon(Icon.createWithResource(
161                         context.getResources(), R.drawable.stat_sys_phone_call))
162                 .setHighlightColor(Color.GREEN)
163                 // TODO: Add icon tint (Color.GREEN)
164                 .setShortDescription("a short description for the sim subscription")
165                 .build());
166 
167         PhoneAccountHandle handle3 = new PhoneAccountHandle(
168                 new ComponentName(context, TestConnectionService.class), SIM_SUBSCRIPTION_ID2);
169         mPhoneAccountMap.put(SIM_SUBSCRIPTION_ID2, handle3);
170         telecomManager.registerPhoneAccount(PhoneAccount.builder(
171                 handle3,
172                 "TelecomTestApp SIM Subscription 2")
173                 .setAddress(Uri.parse("tel:555-TSI2"))
174                 .setSubscriptionAddress(Uri.parse("tel:555-TSI2"))
175                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
176                         PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
177                         PhoneAccount.CAPABILITY_VIDEO_CALLING |
178                         PhoneAccount.CAPABILITY_RTT |
179                         PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)
180                 .setIcon(Icon.createWithResource(
181                         context.getResources(), R.drawable.stat_sys_phone_call))
182                 .setHighlightColor(Color.CYAN)
183                 .setShortDescription("a short description for the sim subscription")
184                 .build());
185 
186         PhoneAccountHandle handle4 = new PhoneAccountHandle(
187                 new ComponentName(context, TestConnectionManager.class), CONNECTION_MANAGER_ID);
188         mPhoneAccountMap.put(CONNECTION_MANAGER_ID, handle4);
189         telecomManager.registerPhoneAccount(PhoneAccount.builder(
190                 handle4,
191                 "TelecomTestApp CONNECTION MANAGER")
192                 .setAddress(Uri.parse("tel:555-CMGR"))
193                 .setSubscriptionAddress(Uri.parse("tel:555-CMGR"))
194                 .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
195                 .setIcon(Icon.createWithResource(
196                         context.getResources(), R.drawable.stat_sys_phone_call))
197                 // TODO: Add icon tint (Color.BLUE)
198                 .setShortDescription("a short description for the connection manager")
199                 .build());
200     }
201 
getPhoneAccountHandle(String id)202     public PhoneAccountHandle getPhoneAccountHandle(String id) {
203         return mPhoneAccountMap.get(id);
204     }
205 
206     /**
207      * Displays all phone accounts registered with telecom.
208      */
showAllPhoneAccounts(Context context)209     public void showAllPhoneAccounts(Context context) {
210         TelecomManager telecomManager =
211                 (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
212         List<PhoneAccountHandle> accounts = telecomManager.getCallCapablePhoneAccounts();
213 
214         Toast.makeText(context, accounts.toString(), Toast.LENGTH_LONG).show();
215     }
216 
217     /**
218      * Returns the system's notification manager needed to add/remove notifications.
219      */
getNotificationManager(Context context)220     private NotificationManager getNotificationManager(Context context) {
221         return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
222     }
223 
224     /**
225      * Creates a notification object for using the telecom APIs.
226      */
getPhoneAccountNotification(Context context)227     private Notification getPhoneAccountNotification(Context context) {
228         final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID);
229         // Both notifications have buttons and only the first one with buttons will show its
230         // buttons.  Since the phone accounts notification is always first, setting false ensures
231         // it can be dismissed to use the other notification.
232         builder.setOngoing(false);
233         builder.setPriority(Notification.PRIORITY_HIGH);
234 
235         final PendingIntent intent = createShowAllPhoneAccountsIntent(context);
236         builder.setContentIntent(intent);
237 
238         builder.setSmallIcon(android.R.drawable.stat_sys_phone_call);
239         // TODO: Consider moving this into a strings.xml
240         builder.setContentText("Test phone accounts via telecom APIs.");
241         builder.setContentTitle("Test Phone Accounts");
242 
243         addRegisterPhoneAccountAction(builder, context);
244         addShowAllPhoneAccountsAction(builder, context);
245 
246         return builder.build();
247     }
248 
249     /**
250      * Creates a notification object out of the current calls state.
251      */
getMainNotification(Context context)252     private Notification getMainNotification(Context context) {
253         final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID);
254         builder.setOngoing(true);
255         builder.setPriority(Notification.PRIORITY_HIGH);
256         builder.setSmallIcon(android.R.drawable.stat_sys_phone_call);
257         builder.setContentText("Test calls via CallService API");
258         builder.setContentTitle("Test Connection Service");
259 
260         addAddOneWayVideoCallAction(builder, context);
261         addAddTwoWayVideoCallAction(builder, context);
262         addAddCallAction(builder, context);
263         addExitAction(builder, context);
264 
265         return builder.build();
266     }
267 
268     /**
269      * Creates the intent to remove the notification.
270      */
createExitIntent(Context context)271     private PendingIntent createExitIntent(Context context) {
272         final Intent intent = new Intent(CallNotificationReceiver.ACTION_CALL_SERVICE_EXIT, null,
273                 context, CallNotificationReceiver.class);
274 
275         return PendingIntent.getBroadcast(context, 0, intent, 0);
276     }
277 
278     /**
279      * Creates the intent to register a phone account.
280      */
createRegisterPhoneAccountIntent(Context context)281     private PendingIntent createRegisterPhoneAccountIntent(Context context) {
282         final Intent intent = new Intent(CallNotificationReceiver.ACTION_REGISTER_PHONE_ACCOUNT,
283                 null, context, CallNotificationReceiver.class);
284         return PendingIntent.getBroadcast(context, 0, intent, 0);
285     }
286 
287     /**
288      * Creates the intent to show all phone accounts.
289      */
createShowAllPhoneAccountsIntent(Context context)290     private PendingIntent createShowAllPhoneAccountsIntent(Context context) {
291         final Intent intent = new Intent(CallNotificationReceiver.ACTION_SHOW_ALL_PHONE_ACCOUNTS,
292                 null, context, CallNotificationReceiver.class);
293         return PendingIntent.getBroadcast(context, 0, intent, 0);
294     }
295 
296     /**
297      * Creates the intent to start an incoming 1-way video call (receive-only)
298      */
createOneWayVideoCallIntent(Context context)299     private PendingIntent createOneWayVideoCallIntent(Context context) {
300         final Intent intent = new Intent(CallNotificationReceiver.ACTION_ONE_WAY_VIDEO_CALL,
301                 null, context, CallNotificationReceiver.class);
302         return PendingIntent.getBroadcast(context, 0, intent, 0);
303     }
304 
305     /**
306      * Creates the intent to start an incoming 2-way video call
307      */
createTwoWayVideoCallIntent(Context context)308     private PendingIntent createTwoWayVideoCallIntent(Context context) {
309         final Intent intent = new Intent(CallNotificationReceiver.ACTION_TWO_WAY_VIDEO_CALL,
310                 null, context, CallNotificationReceiver.class);
311         return PendingIntent.getBroadcast(context, 0, intent, 0);
312     }
313 
314     /**
315      * Creates the intent to start an incoming audio call
316      */
createIncomingAudioCall(Context context)317     private PendingIntent createIncomingAudioCall(Context context) {
318         final Intent intent = new Intent(CallNotificationReceiver.ACTION_AUDIO_CALL,
319                 null, context, CallNotificationReceiver.class);
320         return PendingIntent.getBroadcast(context, 0, intent, 0);
321     }
322 
323     /**
324      * Adds an action to the Notification Builder for adding an incoming call through Telecom.
325      * @param builder The Notification Builder.
326      */
addAddCallAction(Notification.Builder builder, Context context)327     private void addAddCallAction(Notification.Builder builder, Context context) {
328         builder.addAction(0, "Add Call", createIncomingAudioCall(context));
329     }
330 
331     /**
332      * Adds an action to the Notification Builder to add an incoming one-way video call through
333      * Telecom.
334      */
addAddOneWayVideoCallAction(Notification.Builder builder, Context context)335     private void addAddOneWayVideoCallAction(Notification.Builder builder, Context context) {
336         builder.addAction(0, "1-way Video", createOneWayVideoCallIntent(context));
337     }
338 
339     /**
340      * Adds an action to the Notification Builder to add an incoming 2-way video call through
341      * Telecom.
342      */
addAddTwoWayVideoCallAction(Notification.Builder builder, Context context)343     private void addAddTwoWayVideoCallAction(Notification.Builder builder, Context context) {
344         builder.addAction(0, "2-way Video", createTwoWayVideoCallIntent(context));
345     }
346 
347     /**
348      * Adds an action to remove the notification.
349      */
addExitAction(Notification.Builder builder, Context context)350     private void addExitAction(Notification.Builder builder, Context context) {
351         builder.addAction(0, "Exit", createExitIntent(context));
352     }
353 
354     /**
355      * Adds an action to show all registered phone accounts on a device.
356      */
addShowAllPhoneAccountsAction(Notification.Builder builder, Context context)357     private void addShowAllPhoneAccountsAction(Notification.Builder builder, Context context) {
358         builder.addAction(0, "Show Accts", createShowAllPhoneAccountsIntent(context));
359     }
360 
361     /**
362      * Adds an action to register a new phone account.
363      */
addRegisterPhoneAccountAction(Notification.Builder builder, Context context)364     private void addRegisterPhoneAccountAction(Notification.Builder builder, Context context) {
365         builder.addAction(0, "Reg.Acct.", createRegisterPhoneAccountIntent(context));
366     }
367 
shouldStartVideoCall()368     public boolean shouldStartVideoCall() {
369         return mStartVideoCall;
370     }
371 
log(String msg)372     private static void log(String msg) {
373         Log.w("testcallservice", "[CallServiceNotifier] " + msg);
374     }
375 }
376