Merge "Create Public API which exposes if the device is in a state where simultaneous voice and data are possible."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 395dd88..3f224af 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -145,6 +145,7 @@
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL" />
<uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
+ <uses-permission android:name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
<uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
@@ -596,7 +597,9 @@
<action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" />
</intent-filter>
</provider>
- <receiver android:name="com.android.phone.vvm.omtp.sms.OmtpMessageReceiver"
+
+ <receiver
+ android:name="com.android.phone.vvm.VvmSmsReceiver"
android:exported="false"
androidprv:systemUserOnly="true">
<intent-filter>
@@ -656,14 +659,21 @@
</intent-filter>
</receiver>
- <service
- android:name="com.android.phone.vvm.omtp.sms.OmtpProvisioningService"
- android:exported="false" />
<service
android:name="com.android.phone.vvm.omtp.scheduling.TaskSchedulerService"
android:exported="false" />
+ <receiver
+ android:name="com.android.phone.vvm.VvmSimStateTracker"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED"/>
+ <action android:name="android.telephony.action.CARRIER_CONFIG_CHANGED"/>
+ <action android:name="android.intent.action.SIM_STATE_CHANGED"/>
+ </intent-filter>
+ </receiver>
+
<receiver android:name="com.android.phone.vvm.omtp.VvmPackageInstallReceiver"
androidprv:systemUserOnly="true">
<intent-filter>
@@ -673,10 +683,15 @@
</intent-filter>
</receiver>
- <activity android:name=".settings.VoicemailChangePinActivity"
- android:exported="false"
- android:theme="@style/DialerSettingsLight"
- android:windowSoftInputMode="stateVisible|adjustResize">
- </activity>
+ <activity
+ android:name="com.android.phone.settings.VoicemailChangePinActivity"
+ android:exported="false"
+ android:theme="@style/DialerSettingsLight"
+ android:windowSoftInputMode="stateVisible|adjustResize">
+ </activity>
+
+ <service
+ android:name="com.android.phone.vvm.RemoteVvmTaskManager"
+ android:exported="false"/>
</application>
</manifest>
diff --git a/src/com/android/phone/Assert.java b/src/com/android/phone/Assert.java
index 143e66f..37ccda8 100644
--- a/src/com/android/phone/Assert.java
+++ b/src/com/android/phone/Assert.java
@@ -49,7 +49,11 @@
}
public static void fail() {
- throw new AssertionError("Fail");
+ fail("Fail");
+ }
+
+ public static void fail(String reason) {
+ throw new AssertionError(reason);
}
/**
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 6ba2f37..130e9a8 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -18,8 +18,10 @@
import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
+import android.Manifest.permission;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -53,6 +55,7 @@
import android.telephony.NeighboringCellInfo;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
+import android.telephony.SmsManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyHistogram;
@@ -63,6 +66,7 @@
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
+
import com.android.ims.ImsManager;
import com.android.ims.internal.IImsServiceController;
import com.android.ims.internal.IImsServiceFeatureListener;
@@ -90,8 +94,11 @@
import com.android.internal.util.HexDump;
import com.android.phone.settings.VisualVoicemailSettingsUtil;
import com.android.phone.settings.VoicemailNotificationSettingsUtil;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -2052,11 +2059,33 @@
}
@Override
- public VisualVoicemailSmsFilterSettings getSystemVisualVoicemailSmsFilterSettings(
- String packageName, int subId) {
+ public VisualVoicemailSmsFilterSettings getActiveVisualVoicemailSmsFilterSettings(int subId) {
enforceReadPrivilegedPermission();
return VisualVoicemailSmsFilterConfig
- .getVisualVoicemailSmsFilterSettings(mPhone.getContext(), packageName, subId);
+ .getActiveVisualVoicemailSmsFilterSettings(mPhone.getContext(), subId);
+ }
+
+ @Override
+ public void sendVisualVoicemailSmsForSubscriber(String callingPackage, int subId,
+ String number, int port, String text, PendingIntent sentIntent) {
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ enforceDefaultDialer(callingPackage);
+ enforceSendSmsPermission();
+ // Make the calls as the phone process.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
+ if (port == 0) {
+ smsManager.sendTextMessageWithSelfPermissions(number, null, text,
+ sentIntent, null, false);
+ } else {
+ byte[] data = text.getBytes(StandardCharsets.UTF_8);
+ smsManager.sendDataMessageWithSelfPermissions(number, null,
+ (short) port, data, sentIntent, null);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
* Sets the voice activation state of a given subId.
@@ -3393,6 +3422,28 @@
}
/**
+ * Make sure either called from same process as self (phone) or IPC caller has send SMS
+ * permission.
+ *
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ private void enforceSendSmsPermission() {
+ mApp.enforceCallingOrSelfPermission(permission.SEND_SMS, null);
+ }
+
+ /**
+ * Make sure called from the default dialer
+ *
+ * @throws SecurityException if the caller is not the default dialer
+ */
+ private void enforceDefaultDialer(String callingPackage) {
+ TelecomManager telecomManager = mPhone.getContext().getSystemService(TelecomManager.class);
+ if (!callingPackage.equals(telecomManager.getDefaultDialerPackage())) {
+ throw new SecurityException("Caller not default dialer.");
+ }
+ }
+
+ /**
* Return the application ID for the app type.
*
* @param subId the subscription ID that this request applies to.
diff --git a/src/com/android/phone/VisualVoicemailSmsFilterConfig.java b/src/com/android/phone/VisualVoicemailSmsFilterConfig.java
index 2b2e2f5..2ffb477 100644
--- a/src/com/android/phone/VisualVoicemailSmsFilterConfig.java
+++ b/src/com/android/phone/VisualVoicemailSmsFilterConfig.java
@@ -16,12 +16,15 @@
package com.android.phone;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.util.ArraySet;
+import com.android.phone.vvm.RemoteVvmTaskManager;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -39,6 +42,7 @@
private static final String PREFIX_KEY = "_prefix";
private static final String ORIGINATING_NUMBERS_KEY = "_originating_numbers";
private static final String DESTINATION_PORT_KEY = "_destination_port";
+ private static final String DEFAULT_PACKAGE = "com.android.phone";
public static void enableVisualVoicemailSmsFilter(Context context, String callingPackage,
int subId,
@@ -58,6 +62,21 @@
.apply();
}
+ public static VisualVoicemailSmsFilterSettings getActiveVisualVoicemailSmsFilterSettings(
+ Context context, int subId) {
+ ComponentName componentName = RemoteVvmTaskManager.getRemotePackage(context);
+ String packageName;
+ if (componentName == null) {
+ packageName = DEFAULT_PACKAGE;
+ } else {
+ packageName = componentName.getPackageName();
+ }
+ return getVisualVoicemailSmsFilterSettings(
+ context,
+ packageName,
+ subId);
+ }
+
@Nullable
public static VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings(
Context context,
@@ -75,6 +94,7 @@
VisualVoicemailSmsFilterSettings.DEFAULT_DESTINATION_PORT))
.build();
}
+
private static SharedPreferences getSharedPreferences(Context context) {
return PreferenceManager
.getDefaultSharedPreferences(context.createDeviceProtectedStorageContext());
diff --git a/src/com/android/phone/vvm/RemoteVvmTaskManager.java b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
new file mode 100644
index 0000000..ce6fce6
--- /dev/null
+++ b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.phone.vvm;
+
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.VisualVoicemailService;
+import android.telephony.VisualVoicemailSms;
+
+import com.android.phone.Assert;
+import com.android.phone.vvm.omtp.VvmLog;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * Service to manage tasks issued to the {@link VisualVoicemailService}. This service will bind to
+ * the default dialer on a visual voicemail event if it implements the VisualVoicemailService. The
+ * service will hold all resource for the VisualVoicemailService until {@link
+ * VisualVoicemailService.VisualVoicemailTask#finish()} has been called on all issued tasks.
+ *
+ * If the service is already running it will be reused for new events. The service will stop itself
+ * after all events are handled.
+ */
+public class RemoteVvmTaskManager extends Service {
+
+ private static final String TAG = "RemoteVvmTaskManager";
+
+ private static final String ACTION_START_CELL_SERVICE_CONNECTED =
+ "ACTION_START_CELL_SERVICE_CONNECTED";
+ private static final String ACTION_START_SMS_RECEIVED = "ACTION_START_SMS_RECEIVED";
+ private static final String ACTION_START_SIM_REMOVED = "ACTION_START_SIM_REMOVED";
+
+ // TODO(twyen): track task individually to have time outs.
+ private int mTaskReferenceCount;
+
+ private RemoteServiceConnection mConnection;
+
+ /**
+ * Handles incoming messages from the VisualVoicemailService.
+ */
+ private Messenger mMessenger;
+
+ public static void startCellServiceConnected(Context context,
+ PhoneAccountHandle phoneAccountHandle) {
+ Intent intent = new Intent(ACTION_START_CELL_SERVICE_CONNECTED, null, context,
+ RemoteVvmTaskManager.class);
+ intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+ context.startService(intent);
+ }
+
+ public static void startSmsReceived(Context context, VisualVoicemailSms sms) {
+ Intent intent = new Intent(ACTION_START_SMS_RECEIVED, null, context,
+ RemoteVvmTaskManager.class);
+ intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE,
+ sms.getPhoneAccountHandle());
+ intent.putExtra(VisualVoicemailService.DATA_SMS, sms);
+ context.startService(intent);
+ }
+
+ public static void startSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle) {
+ Intent intent = new Intent(ACTION_START_SIM_REMOVED, null, context,
+ RemoteVvmTaskManager.class);
+ intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+ context.startService(intent);
+ }
+
+ public static boolean hasRemoteService(Context context) {
+ return getRemotePackage(context) != null;
+ }
+
+ public static ComponentName getRemotePackage(Context context) {
+
+ ResolveInfo info = context.getPackageManager()
+ .resolveService(newBindIntent(context), PackageManager.MATCH_ALL);
+ if (info == null) {
+ return null;
+ }
+ return info.getComponentInfo().getComponentName();
+ }
+
+ @Override
+ public void onCreate() {
+ Assert.isMainThread();
+ mMessenger = new Messenger(new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ Assert.isMainThread();
+ switch (msg.what) {
+ case VisualVoicemailService.MSG_TASK_ENDED:
+ mTaskReferenceCount--;
+ checkReference();
+ break;
+ default:
+ VvmLog.wtf(TAG, "unexpected message " + msg.what);
+ }
+ }
+ });
+ }
+
+ @Override
+ public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
+ Assert.isMainThread();
+ mTaskReferenceCount++;
+ switch (intent.getAction()) {
+ case ACTION_START_CELL_SERVICE_CONNECTED:
+ send(VisualVoicemailService.MSG_ON_CELL_SERVICE_CONNECTED, intent.getExtras());
+ break;
+ case ACTION_START_SMS_RECEIVED:
+ send(VisualVoicemailService.MSG_ON_SMS_RECEIVED, intent.getExtras());
+ break;
+ case ACTION_START_SIM_REMOVED:
+ send(VisualVoicemailService.MSG_ON_SIM_REMOVED, intent.getExtras());
+ break;
+ default:
+ Assert.fail("Unexpected action +" + intent.getAction());
+ break;
+ }
+ // Don't rerun service if processed is killed.
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ @Nullable
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ private int getTaskId() {
+ // TODO(twyen): generate unique IDs. Reference counting is used now so it doesn't matter.
+ return 1;
+ }
+
+ /**
+ * Class for interacting with the main interface of the service.
+ */
+ private class RemoteServiceConnection implements ServiceConnection {
+
+ private final Queue<Message> mTaskQueue = new LinkedList<>();
+
+ private boolean mConnected;
+
+ /**
+ * A handler in the VisualVoicemailService
+ */
+ private Messenger mRemoteMessenger;
+
+ public void enqueue(Message message) {
+ mTaskQueue.add(message);
+ if (mConnected) {
+ runQueue();
+ }
+ }
+
+ public boolean isConnected() {
+ return mConnected;
+ }
+
+ public void onServiceConnected(ComponentName className,
+ IBinder service) {
+ mRemoteMessenger = new Messenger(service);
+ mConnected = true;
+ runQueue();
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ mConnection = null;
+ mConnected = false;
+ mRemoteMessenger = null;
+ VvmLog.e(TAG, "Service disconnected, " + mTaskReferenceCount + " tasks dropped.");
+ mTaskReferenceCount = 0;
+ checkReference();
+ }
+
+ private void runQueue() {
+ Assert.isMainThread();
+ Message message = mTaskQueue.poll();
+ while (message != null) {
+ message.replyTo = mMessenger;
+ message.arg1 = getTaskId();
+
+ try {
+ mRemoteMessenger.send(message);
+ } catch (RemoteException e) {
+ VvmLog.e(TAG, "Error sending message to remote service", e);
+ }
+ message = mTaskQueue.poll();
+ }
+ }
+ }
+
+ private void send(int what, Bundle extras) {
+ Assert.isMainThread();
+ Message message = Message.obtain();
+ message.what = what;
+ message.setData(new Bundle(extras));
+ if (mConnection == null) {
+ mConnection = new RemoteServiceConnection();
+ }
+ mConnection.enqueue(message);
+
+ if (!mConnection.isConnected()) {
+ Intent intent = newBindIntent(this);
+ intent.setComponent(getRemotePackage(this));
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ private void checkReference() {
+ if (mTaskReferenceCount == 0) {
+ unbindService(mConnection);
+ mConnection = null;
+ }
+ }
+
+ private static Intent newBindIntent(Context context) {
+ Intent intent = new Intent();
+ intent.setAction(VisualVoicemailService.SERVICE_INTERFACE);
+ TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+ intent.setPackage(telecomManager.getDefaultDialerPackage());
+ return intent;
+ }
+}
diff --git a/src/com/android/phone/vvm/VvmSimStateTracker.java b/src/com/android/phone/vvm/VvmSimStateTracker.java
new file mode 100644
index 0000000..069111a
--- /dev/null
+++ b/src/com/android/phone/vvm/VvmSimStateTracker.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.phone.vvm;
+
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemProperties;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.phone.PhoneUtils;
+import com.android.phone.vvm.omtp.VvmLog;
+import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tracks the status of all inserted SIMs. Will notify {@link RemoteVvmTaskManager} of when a SIM
+ * connected to the service for the first time after it was inserted or the system booted, and when
+ * the SIM is removed. Losing cell signal or entering airplane mode will not cause the connected
+ * event to be triggered again. Reinserting the SIM will trigger the connected event. Changing the
+ * carrier config will also trigger the connected event. Events will be delayed until the device has
+ * been fully booted (and left FBE mode).
+ */
+public class VvmSimStateTracker extends BroadcastReceiver {
+
+ private static final String TAG = "VvmSimStateTracker";
+
+ /**
+ * Map to keep track of currently inserted SIMs. If the SIM hasn't been connected to the service
+ * before the value will be a {@link ServiceStateListener} that is still waiting for the
+ * connection. A value of {@code null} means the SIM has been connected to the service before.
+ */
+ private static Map<PhoneAccountHandle, ServiceStateListener> sListeners = new ArrayMap<>();
+
+ /**
+ * Accounts that has events before the device is booted. The events should be regenerated after
+ * the device has fully booted.
+ */
+ private static Set<PhoneAccountHandle> sPreBootHandles = new ArraySet<>();
+
+ /**
+ * Waits for the account to become {@link ServiceState#STATE_IN_SERVICE} and notify the
+ * connected event. Will unregister itself once the event has been triggered.
+ */
+ private class ServiceStateListener extends PhoneStateListener {
+
+ private final PhoneAccountHandle mPhoneAccountHandle;
+ private final Context mContext;
+
+ public ServiceStateListener(Context context, PhoneAccountHandle phoneAccountHandle) {
+ mContext = context;
+ mPhoneAccountHandle = phoneAccountHandle;
+ }
+
+ public void listen() {
+ getTelephonyManager(mContext, mPhoneAccountHandle)
+ .listen(this, PhoneStateListener.LISTEN_SERVICE_STATE);
+ }
+
+ public void unlisten() {
+ getTelephonyManager(mContext, mPhoneAccountHandle)
+ .listen(this, PhoneStateListener.LISTEN_NONE);
+ sListeners.put(mPhoneAccountHandle, null);
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
+ VvmLog.i(TAG, "in service");
+ sendConnected(mContext, mPhoneAccountHandle);
+ unlisten();
+ }
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ final String action = intent.getAction();
+ if (action == null) {
+ VvmLog.w(TAG, "Null action for intent.");
+ return;
+ }
+ VvmLog.i(TAG, action);
+ switch (action) {
+ case Intent.ACTION_BOOT_COMPLETED:
+ onBootCompleted(context);
+ break;
+ case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
+ if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(
+ intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))) {
+ // onSimRemoved will scan all known accounts with isPhoneAccountActive() to find
+ // which SIM is removed.
+ // ACTION_SIM_STATE_CHANGED only provides subId which cannot be converted to a
+ // PhoneAccountHandle when the SIM is absent.
+ onSimRemoved(context);
+ }
+ break;
+ case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
+ int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ VvmLog.i(TAG, "Received SIM change for invalid subscription id.");
+ return;
+ }
+
+ PhoneAccountHandle phoneAccountHandle =
+ PhoneAccountHandleConverter.fromSubId(subId);
+
+ if ("null".equals(phoneAccountHandle.getId())) {
+ VvmLog.e(TAG,
+ "null phone account handle ID, possible modem crash."
+ + " Ignoring carrier config changed event");
+ return;
+ }
+ onCarrierConfigChanged(context, phoneAccountHandle);
+ }
+ }
+
+ private void onBootCompleted(Context context) {
+ for (PhoneAccountHandle phoneAccountHandle : sPreBootHandles) {
+ TelephonyManager telephonyManager = getTelephonyManager(context, phoneAccountHandle);
+ if (telephonyManager == null) {
+ continue;
+ }
+ if (telephonyManager.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) {
+ sListeners.put(phoneAccountHandle, null);
+ sendConnected(context, phoneAccountHandle);
+ } else {
+ listenToAccount(context, phoneAccountHandle);
+ }
+ }
+ sPreBootHandles.clear();
+ }
+
+ private void sendConnected(Context context, PhoneAccountHandle phoneAccountHandle) {
+ VvmLog.i(TAG, "Service connected on " + phoneAccountHandle);
+ RemoteVvmTaskManager.startCellServiceConnected(context, phoneAccountHandle);
+ }
+
+ private void onSimRemoved(Context context) {
+ SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
+ if (!isBootCompleted()) {
+ for (PhoneAccountHandle phoneAccountHandle : sPreBootHandles) {
+ if (!PhoneUtils.isPhoneAccountActive(subscriptionManager, phoneAccountHandle)) {
+ sPreBootHandles.remove(phoneAccountHandle);
+ }
+ }
+ return;
+ }
+ Set<PhoneAccountHandle> removeList = new ArraySet<>();
+ for (PhoneAccountHandle phoneAccountHandle : sListeners.keySet()) {
+ if (!PhoneUtils.isPhoneAccountActive(subscriptionManager, phoneAccountHandle)) {
+ removeList.add(phoneAccountHandle);
+ ServiceStateListener listener = sListeners.get(phoneAccountHandle);
+ if (listener != null) {
+ listener.unlisten();
+ }
+ sendSimRemoved(context, phoneAccountHandle);
+ }
+ }
+
+ for (PhoneAccountHandle phoneAccountHandle : removeList) {
+ sListeners.remove(phoneAccountHandle);
+ }
+ }
+
+ private boolean isBootCompleted() {
+ return SystemProperties.getBoolean("sys.boot_completed", false);
+ }
+
+ private void sendSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle) {
+ VvmLog.i(TAG, "Sim removed on " + phoneAccountHandle);
+ RemoteVvmTaskManager.startSimRemoved(context, phoneAccountHandle);
+ }
+
+ private void onCarrierConfigChanged(Context context, PhoneAccountHandle phoneAccountHandle) {
+ if (!isBootCompleted()) {
+ sPreBootHandles.add(phoneAccountHandle);
+ return;
+ }
+ if (getTelephonyManager(context, phoneAccountHandle).getServiceState().getState()
+ == ServiceState.STATE_IN_SERVICE) {
+ sendConnected(context, phoneAccountHandle);
+ sListeners.put(phoneAccountHandle, null);
+ } else {
+ listenToAccount(context, phoneAccountHandle);
+ }
+ }
+
+ private void listenToAccount(Context context, PhoneAccountHandle phoneAccountHandle) {
+ ServiceStateListener listener = new ServiceStateListener(context, phoneAccountHandle);
+ listener.listen();
+ sListeners.put(phoneAccountHandle, listener);
+ }
+
+ @Nullable
+ private static TelephonyManager getTelephonyManager(Context context,
+ PhoneAccountHandle phoneAccountHandle) {
+ return context.getSystemService(TelephonyManager.class)
+ .createForPhoneAccountHandle(phoneAccountHandle);
+ }
+}
diff --git a/src/com/android/phone/vvm/VvmSmsReceiver.java b/src/com/android/phone/vvm/VvmSmsReceiver.java
new file mode 100644
index 0000000..91e7933
--- /dev/null
+++ b/src/com/android/phone/vvm/VvmSmsReceiver.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.phone.vvm;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.VoicemailContract;
+import android.telephony.VisualVoicemailSms;
+
+import com.android.phone.vvm.omtp.VvmLog;
+import com.android.phone.vvm.omtp.sms.OmtpMessageReceiver;
+
+/**
+ * Receives the SMS filtered by {@link com.android.internal.telephony.VisualVoicemailSmsFilter} and
+ * redirect it to the visual voicemail client. The redirection is required to let telephony service
+ * handle tasks with {@link RemoteVvmTaskManager}
+ */
+public class VvmSmsReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "VvmSmsReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ VisualVoicemailSms sms = intent.getExtras()
+ .getParcelable(VoicemailContract.EXTRA_VOICEMAIL_SMS);
+
+ if (sms.getPhoneAccountHandle() == null) {
+ // This should never happen
+ VvmLog.e(TAG, "Received message for null phone account");
+ return;
+ }
+
+ if (RemoteVvmTaskManager.hasRemoteService(context)) {
+ VvmLog.i(TAG, "Sending SMS received event to remote service");
+ RemoteVvmTaskManager.startSmsReceived(context, sms);
+ return;
+ }
+
+ OmtpMessageReceiver
+ .onReceive(context, sms);
+ }
+}
diff --git a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
index a506a12..0f93f01 100644
--- a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
@@ -32,6 +32,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.phone.settings.VisualVoicemailSettingsUtil;
+import com.android.phone.vvm.RemoteVvmTaskManager;
import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
@@ -55,6 +56,10 @@
return;
}
+ if (RemoteVvmTaskManager.hasRemoteService(context)) {
+ return;
+ }
+
switch (action) {
case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(
@@ -91,6 +96,7 @@
}
VvmLog.d(TAG, "Carrier config changed");
+
if (UserManager.get(context).isUserUnlocked() && !isCryptKeeperMode()) {
processSubId(context, subId);
} else {
diff --git a/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java b/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
index 9809c64..21cce31 100644
--- a/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
+++ b/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
@@ -22,6 +22,7 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.SubscriptionManager;
+import com.android.phone.vvm.RemoteVvmTaskManager;
import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
/**
@@ -45,6 +46,10 @@
return;
}
+ if (RemoteVvmTaskManager.hasRemoteService(context)) {
+ return;
+ }
+
VvmLog.v(TAG, "processing subId list");
for (PhoneAccountHandle handle : TelecomManager.from(context)
.getCallCapablePhoneAccounts()) {
diff --git a/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java b/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
index 7e2472b..31c173d 100644
--- a/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
+++ b/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
@@ -20,9 +20,9 @@
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
-
import com.android.phone.PhoneUtils;
import com.android.phone.VoicemailStatus;
+import com.android.phone.vvm.RemoteVvmTaskManager;
import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
import com.android.phone.vvm.omtp.sync.OmtpVvmSyncService;
import com.android.phone.vvm.omtp.sync.SyncTask;
@@ -55,6 +55,10 @@
return;
}
+ if (RemoteVvmTaskManager.hasRemoteService(mContext)) {
+ return;
+ }
+
int state = serviceState.getState();
if (state == mPreviousState || (state != ServiceState.STATE_IN_SERVICE
&& mPreviousState != ServiceState.STATE_IN_SERVICE)) {
@@ -67,6 +71,7 @@
OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(mContext, subId);
if (state == ServiceState.STATE_IN_SERVICE) {
+
VoicemailStatusQueryHelper voicemailStatusQueryHelper =
new VoicemailStatusQueryHelper(mContext);
if (voicemailStatusQueryHelper.isVoicemailSourceConfigured(mPhoneAccount)) {
diff --git a/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java b/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
index ba5bd70..6a5f349 100644
--- a/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
+++ b/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
@@ -17,10 +17,8 @@
package com.android.phone.vvm.omtp.sms;
import android.content.Context;
-import android.content.Intent;
import android.os.Bundle;
-import android.provider.VoicemailContract;
-import android.telecom.PhoneAccountHandle;
+import android.telephony.VisualVoicemailSms;
import com.android.internal.telephony.Phone;
import com.android.phone.PhoneUtils;
@@ -37,15 +35,14 @@
private static final String TAG = "LegacyModeSmsHandler";
- public static void handle(Context context, Intent intent, PhoneAccountHandle handle) {
+ public static void handle(Context context, VisualVoicemailSms sms) {
VvmLog.v(TAG, "processing VVM SMS on legacy mode");
- String eventType = intent.getExtras()
- .getString(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX);
- Bundle data = intent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS);
+ String eventType = sms.getPrefix();
+ Bundle data = sms.getFields();
if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
SyncMessage message = new SyncMessage(data);
- VvmLog.v(TAG, "Received SYNC sms for " + handle.getId() +
+ VvmLog.v(TAG, "Received SYNC sms for " + sms.getPhoneAccountHandle() +
" with event " + message.getSyncTriggerEvent());
switch (message.getSyncTriggerEvent()) {
@@ -56,7 +53,8 @@
// For some carriers new message count could be set to 0 even if there are still
// unread messages, to clear the message waiting indicator.
VvmLog.v(TAG, "updating MWI");
- Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(handle);
+ Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(
+ sms.getPhoneAccountHandle());
// Setting voicemail message count to non-zero will show the telephony voicemail
// notification, and zero will clear it.
phone.setVoiceMessageCount(message.getNewMessageCount());
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index 397caf8..d42e70e 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -15,7 +15,6 @@
*/
package com.android.phone.vvm.omtp.sms;
-import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
@@ -25,6 +24,8 @@
import android.provider.VoicemailContract;
import android.telecom.PhoneAccountHandle;
import android.telecom.Voicemail;
+import android.telephony.VisualVoicemailSms;
+
import com.android.phone.settings.VisualVoicemailSettingsUtil;
import com.android.phone.vvm.omtp.ActivationTask;
import com.android.phone.vvm.omtp.OmtpConstants;
@@ -40,46 +41,35 @@
/**
* Receive SMS messages and send for processing by the OMTP visual voicemail source.
*/
-public class OmtpMessageReceiver extends BroadcastReceiver {
+public class OmtpMessageReceiver {
private static final String TAG = "OmtpMessageReceiver";
- private Context mContext;
+ public static void onReceive(Context context, VisualVoicemailSms sms) {
- @Override
- public void onReceive(Context context, Intent intent) {
- mContext = context;
- int subId = intent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID);
- PhoneAccountHandle phone = PhoneAccountHandleConverter.fromSubId(subId);
-
- if (phone == null) {
- // This should never happen
- VvmLog.i(TAG, "Received message for null phone account on subId " + subId);
- return;
- }
-
+ PhoneAccountHandle phone = sms.getPhoneAccountHandle();
+ int subId = PhoneAccountHandleConverter.toSubId(phone);
if (!UserManager.get(context).isUserUnlocked()) {
VvmLog.i(TAG, "Received message on locked device");
// LegacyModeSmsHandler can handle new message notifications without storage access
- LegacyModeSmsHandler.handle(context, intent, phone);
+ LegacyModeSmsHandler.handle(context, sms);
// A full sync will happen after the device is unlocked, so nothing else need to be
// done.
return;
}
- OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(mContext, subId);
- if (!VisualVoicemailSettingsUtil.isEnabled(mContext, phone)) {
+ OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(context, subId);
+ if (!VisualVoicemailSettingsUtil.isEnabled(context, phone)) {
if (helper.isLegacyModeEnabled()) {
- LegacyModeSmsHandler.handle(context, intent, phone);
+ LegacyModeSmsHandler.handle(context, sms);
} else {
VvmLog.i(TAG, "Received vvm message for disabled vvm source.");
}
return;
}
- String eventType = intent.getExtras()
- .getString(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX);
- Bundle data = intent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS);
+ String eventType = sms.getPrefix();
+ Bundle data = sms.getFields();
if (eventType == null || data == null) {
VvmLog.e(TAG, "Unparsable VVM SMS received, ignoring");
@@ -91,7 +81,7 @@
VvmLog.v(TAG, "Received SYNC sms for " + subId +
" with event " + message.getSyncTriggerEvent());
- processSync(phone, message);
+ processSync(context, phone, message);
} else if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) {
VvmLog.v(TAG, "Received Status sms for " + subId);
// If the STATUS SMS is initiated by ActivationTask the TaskSchedulerService will reject
@@ -122,7 +112,8 @@
*
* @param message The sync message to extract data from.
*/
- private void processSync(PhoneAccountHandle phone, SyncMessage message) {
+ private static void processSync(Context context, PhoneAccountHandle phone,
+ SyncMessage message) {
Intent serviceIntent = null;
switch (message.getSyncTriggerEvent()) {
case OmtpConstants.NEW_MESSAGE:
@@ -137,18 +128,18 @@
.setPhoneAccount(phone)
.setSourceData(message.getId())
.setDuration(message.getLength())
- .setSourcePackage(mContext.getPackageName());
+ .setSourcePackage(context.getPackageName());
Voicemail voicemail = builder.build();
- VoicemailsQueryHelper queryHelper = new VoicemailsQueryHelper(mContext);
+ VoicemailsQueryHelper queryHelper = new VoicemailsQueryHelper(context);
if (queryHelper.isVoicemailUnique(voicemail)) {
- Uri uri = VoicemailContract.Voicemails.insert(mContext, voicemail);
+ Uri uri = VoicemailContract.Voicemails.insert(context, voicemail);
voicemail = builder.setId(ContentUris.parseId(uri)).setUri(uri).build();
- SyncOneTask.start(mContext, phone, voicemail);
+ SyncOneTask.start(context, phone, voicemail);
}
break;
case OmtpConstants.MAILBOX_UPDATE:
- SyncTask.start(mContext, phone, OmtpVvmSyncService.SYNC_DOWNLOAD_ONLY);
+ SyncTask.start(context, phone, OmtpVvmSyncService.SYNC_DOWNLOAD_ONLY);
break;
case OmtpConstants.GREETINGS_UPDATE:
// Not implemented in V1
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpProvisioningService.java b/src/com/android/phone/vvm/omtp/sms/OmtpProvisioningService.java
deleted file mode 100644
index 154eeeb..0000000
--- a/src/com/android/phone/vvm/omtp/sms/OmtpProvisioningService.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.phone.vvm.omtp.sms;
-
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.provider.VoicemailContract;
-import android.telecom.PhoneAccountHandle;
-
-import com.android.phone.PhoneUtils;
-import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
-import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
-
-/**
- * Performs visual voicemail provisioning in background thread. Not exported.
- */
-public class OmtpProvisioningService extends IntentService {
-
- public OmtpProvisioningService() {
- super("OmtpProvisioningService");
- }
-
- /**
- * Create an intent to start OmtpProvisioningService from a {@link
- * VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED} intent.
- */
- public static Intent getProvisionIntent(Context context, Intent messageIntent) {
- Intent serviceIntent = new Intent(context, OmtpProvisioningService.class);
-
- serviceIntent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID,
- messageIntent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID));
- serviceIntent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS,
- messageIntent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS));
-
- return serviceIntent;
- }
-
- @Override
- public void onHandleIntent(Intent intent) {
- int subId = intent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID);
- PhoneAccountHandle phone = PhoneAccountHandleConverter.fromSubId(subId);
-
- Bundle data = intent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS);
-
- StatusMessage message = new StatusMessage(data);
- startProvisioning(phone, message, data);
- }
-
- private void startProvisioning(PhoneAccountHandle phone, StatusMessage message, Bundle data) {
- OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(this,
- PhoneUtils.getSubIdForPhoneAccountHandle(phone));
-
- }
-}
diff --git a/src/com/android/phone/vvm/omtp/sms/StatusSmsFetcher.java b/src/com/android/phone/vvm/omtp/sms/StatusSmsFetcher.java
index 69e4f5f..adaa2f4 100644
--- a/src/com/android/phone/vvm/omtp/sms/StatusSmsFetcher.java
+++ b/src/com/android/phone/vvm/omtp/sms/StatusSmsFetcher.java
@@ -27,13 +27,16 @@
import android.content.IntentFilter;
import android.os.Bundle;
import android.provider.VoicemailContract;
+import android.telecom.PhoneAccountHandle;
import android.telephony.SmsManager;
+import android.telephony.VisualVoicemailSms;
import com.android.phone.Assert;
import com.android.phone.vvm.omtp.OmtpConstants;
import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
import com.android.phone.vvm.omtp.VvmLog;
import com.android.phone.vvm.omtp.protocol.VisualVoicemailProtocol;
+import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
import java.io.Closeable;
import java.io.IOException;
@@ -107,16 +110,17 @@
return;
}
- int subId = intent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID);
-
+ VisualVoicemailSms sms = intent.getExtras()
+ .getParcelable(VoicemailContract.EXTRA_VOICEMAIL_SMS);
+ PhoneAccountHandle phoneAccountHandle = sms.getPhoneAccountHandle();
+ int subId = PhoneAccountHandleConverter.toSubId(phoneAccountHandle);
if (mSubId != subId) {
return;
}
- String eventType = intent.getExtras()
- .getString(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX);
+ String eventType = sms.getPrefix();
if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) {
- mFuture.complete(intent.getBundleExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS));
+ mFuture.complete(sms.getFields());
return;
}
@@ -132,7 +136,7 @@
return;
}
Bundle translatedBundle = protocol.translateStatusSmsBundle(helper, eventType,
- intent.getBundleExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS));
+ sms.getFields());
if (translatedBundle != null) {
VvmLog.i(TAG, "Translated to STATUS SMS");