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 // TODO(twyen): track task individually to have time outs. 73 private int mTaskReferenceCount; 74 75 private RemoteServiceConnection mConnection; 76 77 /** 78 * Handles incoming messages from the VisualVoicemailService. 79 */ 80 private Messenger mMessenger; 81 82 public static void startCellServiceConnected(Context context, 83 PhoneAccountHandle phoneAccountHandle) { 84 Intent intent = new Intent(ACTION_START_CELL_SERVICE_CONNECTED, null, context, 85 RemoteVvmTaskManager.class); 86 intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); 87 context.startService(intent); 88 } 89 90 public static void startSmsReceived(Context context, VisualVoicemailSms sms) { 91 Intent intent = new Intent(ACTION_START_SMS_RECEIVED, null, context, 92 RemoteVvmTaskManager.class); 93 intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, 94 sms.getPhoneAccountHandle()); 95 intent.putExtra(VisualVoicemailService.DATA_SMS, sms); 96 context.startService(intent); 97 } 98 99 public static void startSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle) { 100 Intent intent = new Intent(ACTION_START_SIM_REMOVED, null, context, 101 RemoteVvmTaskManager.class); 102 intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); 103 context.startService(intent); 104 } 105 106 public static boolean hasRemoteService(Context context, int subId) { 107 return getRemotePackage(context, subId) != null; 108 } 109 110 @Nullable 111 public static ComponentName getRemotePackage(Context context, int subId) { 112 ComponentName broadcastPackage = getBroadcastPackage(context); 113 if (broadcastPackage != null) { 114 return broadcastPackage; 115 } 116 117 Intent bindIntent = newBindIntent(context); 118 119 TelecomManager telecomManager = context.getSystemService(TelecomManager.class); 120 List<String> packages = new ArrayList<>(); 121 packages.add(telecomManager.getDefaultDialerPackage()); 122 PersistableBundle carrierConfig = context 123 .getSystemService(CarrierConfigManager.class).getConfigForSubId(subId); 124 packages.add( 125 carrierConfig.getString(CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING)); 126 String[] vvmPackages = carrierConfig 127 .getStringArray(CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY); 128 if (vvmPackages != null && vvmPackages.length > 0) { 129 for (String packageName : vvmPackages) { 130 packages.add(packageName); 131 } 132 } 133 packages.add(context.getResources().getString(R.string.system_visual_voicemail_client)); 134 packages.add(telecomManager.getSystemDialerPackage()); 135 for (String packageName : packages) { 136 if (TextUtils.isEmpty(packageName)) { 137 continue; 138 } 139 bindIntent.setPackage(packageName); 140 ResolveInfo info = context.getPackageManager() 141 .resolveService(bindIntent, PackageManager.MATCH_ALL); 142 if (info == null) { 143 continue; 144 } 145 if (info.serviceInfo == null) { 146 VvmLog.w(TAG, 147 "Component " + info.getComponentInfo() + " is not a service, ignoring"); 148 continue; 149 } 150 if (!android.Manifest.permission.BIND_VISUAL_VOICEMAIL_SERVICE 151 .equals(info.serviceInfo.permission)) { 152 VvmLog.w(TAG, "package " + info.serviceInfo.packageName 153 + " does not enforce BIND_VISUAL_VOICEMAIL_SERVICE, ignoring"); 154 continue; 155 } 156 157 return info.getComponentInfo().getComponentName(); 158 159 } 160 return null; 161 } 162 163 @Nullable 164 private static ComponentName getBroadcastPackage(Context context) { 165 Intent broadcastIntent = new Intent(ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT); 166 broadcastIntent.setPackage( 167 context.getSystemService(TelecomManager.class).getDefaultDialerPackage()); 168 List<ResolveInfo> info = context.getPackageManager() 169 .queryBroadcastReceivers(broadcastIntent, PackageManager.MATCH_ALL); 170 if (info == null) { 171 return null; 172 } 173 if (info.isEmpty()) { 174 return null; 175 } 176 return info.get(0).getComponentInfo().getComponentName(); 177 } 178 179 @Override 180 public void onCreate() { 181 Assert.isMainThread(); 182 mMessenger = new Messenger(new Handler() { 183 @Override 184 public void handleMessage(Message msg) { 185 Assert.isMainThread(); 186 switch (msg.what) { 187 case VisualVoicemailService.MSG_TASK_ENDED: 188 mTaskReferenceCount--; 189 checkReference(); 190 break; 191 default: 192 VvmLog.wtf(TAG, "unexpected message " + msg.what); 193 } 194 } 195 }); 196 } 197 198 @Override 199 public int onStartCommand(@Nullable Intent intent, int flags, int startId) { 200 Assert.isMainThread(); 201 mTaskReferenceCount++; 202 203 PhoneAccountHandle phoneAccountHandle = intent.getExtras() 204 .getParcelable(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE); 205 int subId = PhoneAccountHandleConverter.toSubId(phoneAccountHandle); 206 ComponentName remotePackage = getRemotePackage(this, subId); 207 if (remotePackage == null) { 208 VvmLog.i(TAG, "No service to handle " + intent.getAction() + ", ignoring"); 209 checkReference(); 210 return START_NOT_STICKY; 211 } 212 213 switch (intent.getAction()) { 214 case ACTION_START_CELL_SERVICE_CONNECTED: 215 send(remotePackage, VisualVoicemailService.MSG_ON_CELL_SERVICE_CONNECTED, 216 intent.getExtras()); 217 break; 218 case ACTION_START_SMS_RECEIVED: 219 send(remotePackage, VisualVoicemailService.MSG_ON_SMS_RECEIVED, intent.getExtras()); 220 break; 221 case ACTION_START_SIM_REMOVED: 222 send(remotePackage, VisualVoicemailService.MSG_ON_SIM_REMOVED, intent.getExtras()); 223 break; 224 default: 225 Assert.fail("Unexpected action +" + intent.getAction()); 226 break; 227 } 228 // Don't rerun service if processed is killed. 229 return START_NOT_STICKY; 230 } 231 232 @Override 233 @Nullable 234 public IBinder onBind(Intent intent) { 235 return null; 236 } 237 238 private int getTaskId() { 239 // TODO(twyen): generate unique IDs. Reference counting is used now so it doesn't matter. 240 return 1; 241 } 242 243 /** 244 * Class for interacting with the main interface of the service. 245 */ 246 private class RemoteServiceConnection implements ServiceConnection { 247 248 private final Queue<Message> mTaskQueue = new LinkedList<>(); 249 250 private boolean mConnected; 251 252 /** 253 * A handler in the VisualVoicemailService 254 */ 255 private Messenger mRemoteMessenger; 256 257 public void enqueue(Message message) { 258 mTaskQueue.add(message); 259 if (mConnected) { 260 runQueue(); 261 } 262 } 263 264 public boolean isConnected() { 265 return mConnected; 266 } 267 268 public void onServiceConnected(ComponentName className, 269 IBinder service) { 270 mRemoteMessenger = new Messenger(service); 271 mConnected = true; 272 runQueue(); 273 } 274 275 public void onServiceDisconnected(ComponentName className) { 276 mConnection = null; 277 mConnected = false; 278 mRemoteMessenger = null; 279 VvmLog.e(TAG, "Service disconnected, " + mTaskReferenceCount + " tasks dropped."); 280 mTaskReferenceCount = 0; 281 checkReference(); 282 } 283 284 private void runQueue() { 285 Assert.isMainThread(); 286 Message message = mTaskQueue.poll(); 287 while (message != null) { 288 message.replyTo = mMessenger; 289 message.arg1 = getTaskId(); 290 291 try { 292 mRemoteMessenger.send(message); 293 } catch (RemoteException e) { 294 VvmLog.e(TAG, "Error sending message to remote service", e); 295 } 296 message = mTaskQueue.poll(); 297 } 298 } 299 } 300 301 private void send(ComponentName remotePackage, int what, Bundle extras) { 302 Assert.isMainThread(); 303 304 if (getBroadcastPackage(this) != null) { 305 /* 306 * Temporarily use a broadcast to notify dialer VVM events instead of using the 307 * VisualVoicemailService. 308 * b/35766990 The VisualVoicemailService is undergoing API changes. The dialer is in 309 * a different repository so it can not be updated in sync with android SDK. It is also 310 * hard to make a manifest service to work in the intermittent state. 311 */ 312 VvmLog.i(TAG, "sending broadcast " + what + " to " + remotePackage); 313 Intent intent = new Intent(ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT); 314 intent.putExtras(extras); 315 intent.putExtra(EXTRA_WHAT, what); 316 intent.setComponent(remotePackage); 317 sendBroadcast(intent); 318 return; 319 } 320 321 Message message = Message.obtain(); 322 message.what = what; 323 message.setData(new Bundle(extras)); 324 if (mConnection == null) { 325 mConnection = new RemoteServiceConnection(); 326 } 327 mConnection.enqueue(message); 328 329 if (!mConnection.isConnected()) { 330 Intent intent = newBindIntent(this); 331 intent.setComponent(remotePackage); 332 VvmLog.i(TAG, "Binding to " + intent.getComponent()); 333 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 334 } 335 } 336 337 private void checkReference() { 338 if (mConnection == null) { 339 return; 340 } 341 if (mTaskReferenceCount == 0) { 342 unbindService(mConnection); 343 mConnection = null; 344 } 345 } 346 347 private static Intent newBindIntent(Context context) { 348 Intent intent = new Intent(); 349 intent.setAction(VisualVoicemailService.SERVICE_INTERFACE); 350 return intent; 351 } 352 } 353