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