• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.phone.vvm;
18 
19 import android.annotation.Nullable;
20 import android.app.Service;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.content.pm.PackageManager;
26 import android.content.pm.ResolveInfo;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Message;
31 import android.os.Messenger;
32 import android.os.PersistableBundle;
33 import android.os.RemoteException;
34 import android.telecom.PhoneAccountHandle;
35 import android.telecom.TelecomManager;
36 import android.telephony.CarrierConfigManager;
37 import android.telephony.VisualVoicemailService;
38 import android.telephony.VisualVoicemailSms;
39 import android.text.TextUtils;
40 
41 import com.android.phone.Assert;
42 import com.android.phone.R;
43 
44 import java.util.ArrayList;
45 import java.util.LinkedList;
46 import java.util.List;
47 import java.util.Queue;
48 
49 /**
50  * Service to manage tasks issued to the {@link VisualVoicemailService}. This service will bind to
51  * the default dialer on a visual voicemail event if it implements the VisualVoicemailService. The
52  * service will hold all resource for the VisualVoicemailService until {@link
53  * VisualVoicemailService.VisualVoicemailTask#finish()} has been called on all issued tasks.
54  *
55  * If the service is already running it will be reused for new events. The service will stop itself
56  * after all events are handled.
57  */
58 public class RemoteVvmTaskManager extends Service {
59 
60     private static final String TAG = "RemoteVvmTaskManager";
61 
62     private static final String ACTION_START_CELL_SERVICE_CONNECTED =
63             "ACTION_START_CELL_SERVICE_CONNECTED";
64     private static final String ACTION_START_SMS_RECEIVED = "ACTION_START_SMS_RECEIVED";
65     private static final String ACTION_START_SIM_REMOVED = "ACTION_START_SIM_REMOVED";
66 
67     // TODO(b/35766990): Remove after VisualVoicemailService API is stabilized.
68     private static final String ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT =
69             "com.android.phone.vvm.ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT";
70     private static final String EXTRA_WHAT = "what";
71 
72     // TODO(twyen): track task individually to have time outs.
73     private int mTaskReferenceCount;
74 
75     private RemoteServiceConnection mConnection;
76 
77     /**
78      * Handles incoming messages from the VisualVoicemailService.
79      */
80     private Messenger mMessenger;
81 
startCellServiceConnected(Context context, PhoneAccountHandle phoneAccountHandle)82     public static void startCellServiceConnected(Context context,
83             PhoneAccountHandle phoneAccountHandle) {
84         Intent intent = new Intent(ACTION_START_CELL_SERVICE_CONNECTED, null, context,
85                 RemoteVvmTaskManager.class);
86         intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
87         context.startService(intent);
88     }
89 
startSmsReceived(Context context, VisualVoicemailSms sms)90     public static void startSmsReceived(Context context, VisualVoicemailSms sms) {
91         Intent intent = new Intent(ACTION_START_SMS_RECEIVED, null, context,
92                 RemoteVvmTaskManager.class);
93         intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE,
94                 sms.getPhoneAccountHandle());
95         intent.putExtra(VisualVoicemailService.DATA_SMS, sms);
96         context.startService(intent);
97     }
98 
startSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle)99     public static void startSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle) {
100         Intent intent = new Intent(ACTION_START_SIM_REMOVED, null, context,
101                 RemoteVvmTaskManager.class);
102         intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
103         context.startService(intent);
104     }
105 
hasRemoteService(Context context, int subId)106     public static boolean hasRemoteService(Context context, int subId) {
107         return getRemotePackage(context, subId) != null;
108     }
109 
110     @Nullable
getRemotePackage(Context context, int subId)111     public static ComponentName getRemotePackage(Context context, int subId) {
112         ComponentName broadcastPackage = getBroadcastPackage(context);
113         if (broadcastPackage != null) {
114             return broadcastPackage;
115         }
116 
117         Intent bindIntent = newBindIntent(context);
118 
119         TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
120         List<String> packages = new ArrayList<>();
121         packages.add(telecomManager.getDefaultDialerPackage());
122         PersistableBundle carrierConfig = context
123                 .getSystemService(CarrierConfigManager.class).getConfigForSubId(subId);
124         packages.add(
125                 carrierConfig.getString(CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING));
126         String[] vvmPackages = carrierConfig
127                 .getStringArray(CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY);
128         if (vvmPackages != null && vvmPackages.length > 0) {
129             for (String packageName : vvmPackages) {
130                 packages.add(packageName);
131             }
132         }
133         packages.add(context.getResources().getString(R.string.system_visual_voicemail_client));
134         packages.add(telecomManager.getSystemDialerPackage());
135         for (String packageName : packages) {
136             if (TextUtils.isEmpty(packageName)) {
137                 continue;
138             }
139             bindIntent.setPackage(packageName);
140             ResolveInfo info = context.getPackageManager()
141                     .resolveService(bindIntent, PackageManager.MATCH_ALL);
142             if (info == null) {
143                 continue;
144             }
145             if (info.serviceInfo == null) {
146                 VvmLog.w(TAG,
147                         "Component " + info.getComponentInfo() + " is not a service, ignoring");
148                 continue;
149             }
150             if (!android.Manifest.permission.BIND_VISUAL_VOICEMAIL_SERVICE
151                     .equals(info.serviceInfo.permission)) {
152                 VvmLog.w(TAG, "package " + info.serviceInfo.packageName
153                         + " does not enforce BIND_VISUAL_VOICEMAIL_SERVICE, ignoring");
154                 continue;
155             }
156 
157             return info.getComponentInfo().getComponentName();
158 
159         }
160         return null;
161     }
162 
163     @Nullable
getBroadcastPackage(Context context)164     private static ComponentName getBroadcastPackage(Context context) {
165         Intent broadcastIntent = new Intent(ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT);
166         broadcastIntent.setPackage(
167                 context.getSystemService(TelecomManager.class).getDefaultDialerPackage());
168         List<ResolveInfo> info = context.getPackageManager()
169                 .queryBroadcastReceivers(broadcastIntent, PackageManager.MATCH_ALL);
170         if (info == null) {
171             return null;
172         }
173         if (info.isEmpty()) {
174             return null;
175         }
176         return info.get(0).getComponentInfo().getComponentName();
177     }
178 
179     @Override
onCreate()180     public void onCreate() {
181         Assert.isMainThread();
182         mMessenger = new Messenger(new Handler() {
183             @Override
184             public void handleMessage(Message msg) {
185                 Assert.isMainThread();
186                 switch (msg.what) {
187                     case VisualVoicemailService.MSG_TASK_ENDED:
188                         mTaskReferenceCount--;
189                         checkReference();
190                         break;
191                     default:
192                         VvmLog.wtf(TAG, "unexpected message " + msg.what);
193                 }
194             }
195         });
196     }
197 
198     @Override
onStartCommand(@ullable Intent intent, int flags, int startId)199     public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
200         Assert.isMainThread();
201         mTaskReferenceCount++;
202 
203         PhoneAccountHandle phoneAccountHandle = intent.getExtras()
204                 .getParcelable(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE);
205         int subId = PhoneAccountHandleConverter.toSubId(phoneAccountHandle);
206         ComponentName remotePackage = getRemotePackage(this, subId);
207         if (remotePackage == null) {
208             VvmLog.i(TAG, "No service to handle " + intent.getAction() + ", ignoring");
209             checkReference();
210             return START_NOT_STICKY;
211         }
212 
213         switch (intent.getAction()) {
214             case ACTION_START_CELL_SERVICE_CONNECTED:
215                 send(remotePackage, VisualVoicemailService.MSG_ON_CELL_SERVICE_CONNECTED,
216                         intent.getExtras());
217                 break;
218             case ACTION_START_SMS_RECEIVED:
219                 send(remotePackage, VisualVoicemailService.MSG_ON_SMS_RECEIVED, intent.getExtras());
220                 break;
221             case ACTION_START_SIM_REMOVED:
222                 send(remotePackage, VisualVoicemailService.MSG_ON_SIM_REMOVED, intent.getExtras());
223                 break;
224             default:
225                 Assert.fail("Unexpected action +" + intent.getAction());
226                 break;
227         }
228         // Don't rerun service if processed is killed.
229         return START_NOT_STICKY;
230     }
231 
232     @Override
233     @Nullable
onBind(Intent intent)234     public IBinder onBind(Intent intent) {
235         return null;
236     }
237 
getTaskId()238     private int getTaskId() {
239         // TODO(twyen): generate unique IDs. Reference counting is used now so it doesn't matter.
240         return 1;
241     }
242 
243     /**
244      * Class for interacting with the main interface of the service.
245      */
246     private class RemoteServiceConnection implements ServiceConnection {
247 
248         private final Queue<Message> mTaskQueue = new LinkedList<>();
249 
250         private boolean mConnected;
251 
252         /**
253          * A handler in the VisualVoicemailService
254          */
255         private Messenger mRemoteMessenger;
256 
enqueue(Message message)257         public void enqueue(Message message) {
258             mTaskQueue.add(message);
259             if (mConnected) {
260                 runQueue();
261             }
262         }
263 
isConnected()264         public boolean isConnected() {
265             return mConnected;
266         }
267 
onServiceConnected(ComponentName className, IBinder service)268         public void onServiceConnected(ComponentName className,
269                 IBinder service) {
270             mRemoteMessenger = new Messenger(service);
271             mConnected = true;
272             runQueue();
273         }
274 
onServiceDisconnected(ComponentName className)275         public void onServiceDisconnected(ComponentName className) {
276             mConnection = null;
277             mConnected = false;
278             mRemoteMessenger = null;
279             VvmLog.e(TAG, "Service disconnected, " + mTaskReferenceCount + " tasks dropped.");
280             mTaskReferenceCount = 0;
281             checkReference();
282         }
283 
runQueue()284         private void runQueue() {
285             Assert.isMainThread();
286             Message message = mTaskQueue.poll();
287             while (message != null) {
288                 message.replyTo = mMessenger;
289                 message.arg1 = getTaskId();
290 
291                 try {
292                     mRemoteMessenger.send(message);
293                 } catch (RemoteException e) {
294                     VvmLog.e(TAG, "Error sending message to remote service", e);
295                 }
296                 message = mTaskQueue.poll();
297             }
298         }
299     }
300 
send(ComponentName remotePackage, int what, Bundle extras)301     private void send(ComponentName remotePackage, int what, Bundle extras) {
302         Assert.isMainThread();
303 
304         if (getBroadcastPackage(this) != null) {
305             /*
306              * Temporarily use a broadcast to notify dialer VVM events instead of using the
307              * VisualVoicemailService.
308              * b/35766990 The VisualVoicemailService is undergoing API changes. The dialer is in
309              * a different repository so it can not be updated in sync with android SDK. It is also
310              * hard to make a manifest service to work in the intermittent state.
311              */
312             VvmLog.i(TAG, "sending broadcast " + what + " to " + remotePackage);
313             Intent intent = new Intent(ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT);
314             intent.putExtras(extras);
315             intent.putExtra(EXTRA_WHAT, what);
316             intent.setComponent(remotePackage);
317             sendBroadcast(intent);
318             return;
319         }
320 
321         Message message = Message.obtain();
322         message.what = what;
323         message.setData(new Bundle(extras));
324         if (mConnection == null) {
325             mConnection = new RemoteServiceConnection();
326         }
327         mConnection.enqueue(message);
328 
329         if (!mConnection.isConnected()) {
330             Intent intent = newBindIntent(this);
331             intent.setComponent(remotePackage);
332             VvmLog.i(TAG, "Binding to " + intent.getComponent());
333             bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
334         }
335     }
336 
checkReference()337     private void checkReference() {
338         if (mConnection == null) {
339             return;
340         }
341         if (mTaskReferenceCount == 0) {
342             unbindService(mConnection);
343             mConnection = null;
344         }
345     }
346 
newBindIntent(Context context)347     private static Intent newBindIntent(Context context) {
348         Intent intent = new Intent();
349         intent.setAction(VisualVoicemailService.SERVICE_INTERFACE);
350         return intent;
351     }
352 }
353