• 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     private static final String EXTRA_TARGET_PACKAGE = "target_package";
73 
74     // TODO(twyen): track task individually to have time outs.
75     private int mTaskReferenceCount;
76 
77     private RemoteServiceConnection mConnection;
78 
79     /**
80      * Handles incoming messages from the VisualVoicemailService.
81      */
82     private Messenger mMessenger;
83 
startCellServiceConnected(Context context, PhoneAccountHandle phoneAccountHandle)84     static void startCellServiceConnected(Context context,
85             PhoneAccountHandle phoneAccountHandle) {
86         Intent intent = new Intent(ACTION_START_CELL_SERVICE_CONNECTED, null, context,
87                 RemoteVvmTaskManager.class);
88         intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
89         context.startService(intent);
90     }
91 
startSmsReceived(Context context, VisualVoicemailSms sms, String targetPackage)92     static void startSmsReceived(Context context, VisualVoicemailSms sms,
93             String targetPackage) {
94         Intent intent = new Intent(ACTION_START_SMS_RECEIVED, null, context,
95                 RemoteVvmTaskManager.class);
96         intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE,
97                 sms.getPhoneAccountHandle());
98         intent.putExtra(VisualVoicemailService.DATA_SMS, sms);
99         intent.putExtra(EXTRA_TARGET_PACKAGE, targetPackage);
100         context.startService(intent);
101     }
102 
startSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle)103     static void startSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle) {
104         Intent intent = new Intent(ACTION_START_SIM_REMOVED, null, context,
105                 RemoteVvmTaskManager.class);
106         intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
107         context.startService(intent);
108     }
109 
hasRemoteService(Context context, int subId, String targetPackage)110     static boolean hasRemoteService(Context context, int subId, String targetPackage) {
111         return getRemotePackage(context, subId, targetPackage) != null;
112     }
113 
114     /**
115      * Return the {@link ComponentName} of the {@link VisualVoicemailService} which is active (the
116      * current default dialer), or {@code null} if no implementation is found.
117      */
118     @Nullable
getRemotePackage(Context context, int subId)119     public static ComponentName getRemotePackage(Context context, int subId) {
120         return getRemotePackage(context, subId, null);
121     }
122 
123     /**
124      * Return the {@link ComponentName} of the {@link VisualVoicemailService} which is active (the
125      * current default dialer), or {@code null} if no implementation is found.
126      *
127      * @param targetPackage the package that should be the active VisualVociemailService
128      */
129     @Nullable
getRemotePackage(Context context, int subId, @Nullable String targetPackage)130     public static ComponentName getRemotePackage(Context context, int subId,
131             @Nullable String targetPackage) {
132         ComponentName broadcastPackage = getBroadcastPackage(context);
133         if (broadcastPackage != null) {
134             return broadcastPackage;
135         }
136 
137         Intent bindIntent = newBindIntent(context);
138 
139         TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
140         List<String> packages = new ArrayList<>();
141         packages.add(telecomManager.getDefaultDialerPackage());
142         // TODO(b/73136824): Check permissions in the calling function and avoid relying on the
143         // binder caller's permissions to access the carrier config.
144         PersistableBundle carrierConfig = context
145                 .getSystemService(CarrierConfigManager.class).getConfigForSubId(subId);
146         packages.add(
147                 carrierConfig
148                         .getString(CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING));
149         String[] vvmPackages = carrierConfig
150                 .getStringArray(CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY);
151         if (vvmPackages != null && vvmPackages.length > 0) {
152             for (String packageName : vvmPackages) {
153                 packages.add(packageName);
154             }
155         }
156         packages.add(context.getResources().getString(R.string.system_visual_voicemail_client));
157         packages.add(telecomManager.getSystemDialerPackage());
158 
159         for (String packageName : packages) {
160             if (TextUtils.isEmpty(packageName)) {
161                 continue;
162             }
163             bindIntent.setPackage(packageName);
164             ResolveInfo info = context.getPackageManager()
165                     .resolveService(bindIntent, PackageManager.MATCH_ALL);
166             if (info == null) {
167                 continue;
168             }
169             if (info.serviceInfo == null) {
170                 VvmLog.w(TAG,
171                         "Component " + info.getComponentInfo() + " is not a service, ignoring");
172                 continue;
173             }
174             if (!android.Manifest.permission.BIND_VISUAL_VOICEMAIL_SERVICE
175                     .equals(info.serviceInfo.permission)) {
176                 VvmLog.w(TAG, "package " + info.serviceInfo.packageName
177                         + " does not enforce BIND_VISUAL_VOICEMAIL_SERVICE, ignoring");
178                 continue;
179             }
180             if (targetPackage != null && !TextUtils.equals(packageName, targetPackage)) {
181                 VvmLog.w(TAG, "target package " + targetPackage
182                         + " is no longer the active VisualVoicemailService, ignoring");
183             }
184             return info.getComponentInfo().getComponentName();
185 
186         }
187         return null;
188     }
189 
190     @Nullable
getBroadcastPackage(Context context)191     private static ComponentName getBroadcastPackage(Context context) {
192         Intent broadcastIntent = new Intent(ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT);
193         broadcastIntent.setPackage(
194                 context.getSystemService(TelecomManager.class).getDefaultDialerPackage());
195         List<ResolveInfo> info = context.getPackageManager()
196                 .queryBroadcastReceivers(broadcastIntent, PackageManager.MATCH_ALL);
197         if (info == null) {
198             return null;
199         }
200         if (info.isEmpty()) {
201             return null;
202         }
203         return info.get(0).getComponentInfo().getComponentName();
204     }
205 
206     @Override
onCreate()207     public void onCreate() {
208         Assert.isMainThread();
209         mMessenger = new Messenger(new Handler() {
210             @Override
211             public void handleMessage(Message msg) {
212                 Assert.isMainThread();
213                 switch (msg.what) {
214                     case VisualVoicemailService.MSG_TASK_ENDED:
215                         mTaskReferenceCount--;
216                         checkReference();
217                         break;
218                     default:
219                         VvmLog.wtf(TAG, "unexpected message " + msg.what);
220                 }
221             }
222         });
223     }
224 
225     @Override
onStartCommand(@ullable Intent intent, int flags, int startId)226     public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
227         Assert.isMainThread();
228         mTaskReferenceCount++;
229 
230         PhoneAccountHandle phoneAccountHandle = intent.getExtras()
231                 .getParcelable(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE);
232         int subId = PhoneAccountHandleConverter.toSubId(phoneAccountHandle);
233         ComponentName remotePackage = getRemotePackage(this, subId,
234                 intent.getStringExtra(EXTRA_TARGET_PACKAGE));
235         if (remotePackage == null) {
236             VvmLog.i(TAG, "No service to handle " + intent.getAction() + ", ignoring");
237             checkReference();
238             return START_NOT_STICKY;
239         }
240 
241         switch (intent.getAction()) {
242             case ACTION_START_CELL_SERVICE_CONNECTED:
243                 send(remotePackage, VisualVoicemailService.MSG_ON_CELL_SERVICE_CONNECTED,
244                         intent.getExtras());
245                 break;
246             case ACTION_START_SMS_RECEIVED:
247                 send(remotePackage, VisualVoicemailService.MSG_ON_SMS_RECEIVED, intent.getExtras());
248                 break;
249             case ACTION_START_SIM_REMOVED:
250                 send(remotePackage, VisualVoicemailService.MSG_ON_SIM_REMOVED, intent.getExtras());
251                 break;
252             default:
253                 Assert.fail("Unexpected action +" + intent.getAction());
254                 break;
255         }
256         // Don't rerun service if processed is killed.
257         return START_NOT_STICKY;
258     }
259 
260     @Override
261     @Nullable
onBind(Intent intent)262     public IBinder onBind(Intent intent) {
263         return null;
264     }
265 
getTaskId()266     private int getTaskId() {
267         // TODO(twyen): generate unique IDs. Reference counting is used now so it doesn't matter.
268         return 1;
269     }
270 
271     /**
272      * Class for interacting with the main interface of the service.
273      */
274     private class RemoteServiceConnection implements ServiceConnection {
275 
276         private final Queue<Message> mTaskQueue = new LinkedList<>();
277 
278         private boolean mConnected;
279 
280         /**
281          * A handler in the VisualVoicemailService
282          */
283         private Messenger mRemoteMessenger;
284 
enqueue(Message message)285         public void enqueue(Message message) {
286             mTaskQueue.add(message);
287             if (mConnected) {
288                 runQueue();
289             }
290         }
291 
isConnected()292         public boolean isConnected() {
293             return mConnected;
294         }
295 
onServiceConnected(ComponentName className, IBinder service)296         public void onServiceConnected(ComponentName className,
297                 IBinder service) {
298             mRemoteMessenger = new Messenger(service);
299             mConnected = true;
300             runQueue();
301         }
302 
onServiceDisconnected(ComponentName className)303         public void onServiceDisconnected(ComponentName className) {
304             mConnection = null;
305             mConnected = false;
306             mRemoteMessenger = null;
307             VvmLog.e(TAG, "Service disconnected, " + mTaskReferenceCount + " tasks dropped.");
308             mTaskReferenceCount = 0;
309             checkReference();
310         }
311 
runQueue()312         private void runQueue() {
313             Assert.isMainThread();
314             Message message = mTaskQueue.poll();
315             while (message != null) {
316                 message.replyTo = mMessenger;
317                 message.arg1 = getTaskId();
318 
319                 try {
320                     mRemoteMessenger.send(message);
321                 } catch (RemoteException e) {
322                     VvmLog.e(TAG, "Error sending message to remote service", e);
323                 }
324                 message = mTaskQueue.poll();
325             }
326         }
327     }
328 
send(ComponentName remotePackage, int what, Bundle extras)329     private void send(ComponentName remotePackage, int what, Bundle extras) {
330         Assert.isMainThread();
331 
332         if (getBroadcastPackage(this) != null) {
333             /*
334              * Temporarily use a broadcast to notify dialer VVM events instead of using the
335              * VisualVoicemailService.
336              * b/35766990 The VisualVoicemailService is undergoing API changes. The dialer is in
337              * a different repository so it can not be updated in sync with android SDK. It is also
338              * hard to make a manifest service to work in the intermittent state.
339              */
340             VvmLog.i(TAG, "sending broadcast " + what + " to " + remotePackage);
341             Intent intent = new Intent(ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT);
342             intent.putExtras(extras);
343             intent.putExtra(EXTRA_WHAT, what);
344             intent.setComponent(remotePackage);
345             sendBroadcast(intent);
346             return;
347         }
348 
349         Message message = Message.obtain();
350         message.what = what;
351         message.setData(new Bundle(extras));
352         if (mConnection == null) {
353             mConnection = new RemoteServiceConnection();
354         }
355         mConnection.enqueue(message);
356 
357         if (!mConnection.isConnected()) {
358             Intent intent = newBindIntent(this);
359             intent.setComponent(remotePackage);
360             VvmLog.i(TAG, "Binding to " + intent.getComponent());
361             bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
362         }
363     }
364 
checkReference()365     private void checkReference() {
366         if (mConnection == null) {
367             return;
368         }
369         if (mTaskReferenceCount == 0) {
370             unbindService(mConnection);
371             mConnection = null;
372         }
373     }
374 
newBindIntent(Context context)375     private static Intent newBindIntent(Context context) {
376         Intent intent = new Intent();
377         intent.setAction(VisualVoicemailService.SERVICE_INTERFACE);
378         return intent;
379     }
380 }
381