• 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().resolveService(bindIntent, 0);
165             if (info == null) {
166                 continue;
167             }
168             if (info.serviceInfo == null) {
169                 VvmLog.w(TAG,
170                         "Component " + info.getComponentInfo() + " is not a service, ignoring");
171                 continue;
172             }
173             if (!android.Manifest.permission.BIND_VISUAL_VOICEMAIL_SERVICE
174                     .equals(info.serviceInfo.permission)) {
175                 VvmLog.w(TAG, "package " + info.serviceInfo.packageName
176                         + " does not enforce BIND_VISUAL_VOICEMAIL_SERVICE, ignoring");
177                 continue;
178             }
179             if (targetPackage != null && !TextUtils.equals(packageName, targetPackage)) {
180                 VvmLog.w(TAG, "target package " + targetPackage
181                         + " is no longer the active VisualVoicemailService, ignoring");
182             }
183             return info.getComponentInfo().getComponentName();
184 
185         }
186         return null;
187     }
188 
189     @Nullable
getBroadcastPackage(Context context)190     private static ComponentName getBroadcastPackage(Context context) {
191         Intent broadcastIntent = new Intent(ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT);
192         broadcastIntent.setPackage(
193                 context.getSystemService(TelecomManager.class).getDefaultDialerPackage());
194         List<ResolveInfo> info = context.getPackageManager()
195                 .queryBroadcastReceivers(broadcastIntent, PackageManager.MATCH_ALL);
196         if (info == null) {
197             return null;
198         }
199         if (info.isEmpty()) {
200             return null;
201         }
202         return info.get(0).getComponentInfo().getComponentName();
203     }
204 
205     @Override
onCreate()206     public void onCreate() {
207         Assert.isMainThread();
208         mMessenger = new Messenger(new Handler() {
209             @Override
210             public void handleMessage(Message msg) {
211                 Assert.isMainThread();
212                 switch (msg.what) {
213                     case VisualVoicemailService.MSG_TASK_ENDED:
214                         mTaskReferenceCount--;
215                         checkReference();
216                         break;
217                     default:
218                         VvmLog.wtf(TAG, "unexpected message " + msg.what);
219                 }
220             }
221         });
222     }
223 
224     @Override
onStartCommand(@ullable Intent intent, int flags, int startId)225     public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
226         Assert.isMainThread();
227         mTaskReferenceCount++;
228 
229         PhoneAccountHandle phoneAccountHandle = intent.getExtras()
230                 .getParcelable(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE);
231         int subId = PhoneAccountHandleConverter.toSubId(phoneAccountHandle);
232         ComponentName remotePackage = getRemotePackage(this, subId,
233                 intent.getStringExtra(EXTRA_TARGET_PACKAGE));
234         if (remotePackage == null) {
235             VvmLog.i(TAG, "No service to handle " + intent.getAction() + ", ignoring");
236             checkReference();
237             return START_NOT_STICKY;
238         }
239 
240         switch (intent.getAction()) {
241             case ACTION_START_CELL_SERVICE_CONNECTED:
242                 send(remotePackage, VisualVoicemailService.MSG_ON_CELL_SERVICE_CONNECTED,
243                         intent.getExtras());
244                 break;
245             case ACTION_START_SMS_RECEIVED:
246                 send(remotePackage, VisualVoicemailService.MSG_ON_SMS_RECEIVED, intent.getExtras());
247                 break;
248             case ACTION_START_SIM_REMOVED:
249                 send(remotePackage, VisualVoicemailService.MSG_ON_SIM_REMOVED, intent.getExtras());
250                 break;
251             default:
252                 Assert.fail("Unexpected action +" + intent.getAction());
253                 break;
254         }
255         // Don't rerun service if processed is killed.
256         return START_NOT_STICKY;
257     }
258 
259     @Override
260     @Nullable
onBind(Intent intent)261     public IBinder onBind(Intent intent) {
262         return null;
263     }
264 
getTaskId()265     private int getTaskId() {
266         // TODO(twyen): generate unique IDs. Reference counting is used now so it doesn't matter.
267         return 1;
268     }
269 
270     /**
271      * Class for interacting with the main interface of the service.
272      */
273     private class RemoteServiceConnection implements ServiceConnection {
274 
275         private final Queue<Message> mTaskQueue = new LinkedList<>();
276 
277         private boolean mConnected;
278 
279         /**
280          * A handler in the VisualVoicemailService
281          */
282         private Messenger mRemoteMessenger;
283 
enqueue(Message message)284         public void enqueue(Message message) {
285             mTaskQueue.add(message);
286             if (mConnected) {
287                 runQueue();
288             }
289         }
290 
isConnected()291         public boolean isConnected() {
292             return mConnected;
293         }
294 
onServiceConnected(ComponentName className, IBinder service)295         public void onServiceConnected(ComponentName className,
296                 IBinder service) {
297             mRemoteMessenger = new Messenger(service);
298             mConnected = true;
299             runQueue();
300         }
301 
onServiceDisconnected(ComponentName className)302         public void onServiceDisconnected(ComponentName className) {
303             mConnection = null;
304             mConnected = false;
305             mRemoteMessenger = null;
306             VvmLog.e(TAG, "Service disconnected, " + mTaskReferenceCount + " tasks dropped.");
307             mTaskReferenceCount = 0;
308             checkReference();
309         }
310 
runQueue()311         private void runQueue() {
312             Assert.isMainThread();
313             Message message = mTaskQueue.poll();
314             while (message != null) {
315                 message.replyTo = mMessenger;
316                 message.arg1 = getTaskId();
317 
318                 try {
319                     mRemoteMessenger.send(message);
320                 } catch (RemoteException e) {
321                     VvmLog.e(TAG, "Error sending message to remote service", e);
322                 }
323                 message = mTaskQueue.poll();
324             }
325         }
326     }
327 
send(ComponentName remotePackage, int what, Bundle extras)328     private void send(ComponentName remotePackage, int what, Bundle extras) {
329         Assert.isMainThread();
330 
331         if (getBroadcastPackage(this) != null) {
332             /*
333              * Temporarily use a broadcast to notify dialer VVM events instead of using the
334              * VisualVoicemailService.
335              * b/35766990 The VisualVoicemailService is undergoing API changes. The dialer is in
336              * a different repository so it can not be updated in sync with android SDK. It is also
337              * hard to make a manifest service to work in the intermittent state.
338              */
339             VvmLog.i(TAG, "sending broadcast " + what + " to " + remotePackage);
340             Intent intent = new Intent(ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT);
341             intent.putExtras(extras);
342             intent.putExtra(EXTRA_WHAT, what);
343             intent.setComponent(remotePackage);
344             sendBroadcast(intent);
345             return;
346         }
347 
348         Message message = Message.obtain();
349         message.what = what;
350         message.setData(new Bundle(extras));
351         if (mConnection == null) {
352             mConnection = new RemoteServiceConnection();
353         }
354         mConnection.enqueue(message);
355 
356         if (!mConnection.isConnected()) {
357             Intent intent = newBindIntent(this);
358             intent.setComponent(remotePackage);
359             VvmLog.i(TAG, "Binding to " + intent.getComponent());
360             bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
361         }
362     }
363 
checkReference()364     private void checkReference() {
365         if (mConnection == null) {
366             return;
367         }
368         if (mTaskReferenceCount == 0) {
369             unbindService(mConnection);
370             mConnection = null;
371         }
372     }
373 
newBindIntent(Context context)374     private static Intent newBindIntent(Context context) {
375         Intent intent = new Intent();
376         intent.setAction(VisualVoicemailService.SERVICE_INTERFACE);
377         return intent;
378     }
379 }
380