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