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