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