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