Merge "Use the synchronous Connection creation API" into lmp-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4d19197..ad8829c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -82,12 +82,15 @@
<!-- Title for setting to select default outgoing phone account -->
<string name="default_outgoing_account_title">Default outgoing account</string>
- <!-- Summary for setting to select default outgoing phone account -->
- <string name="default_outgoing_account_summary">Select a phone account to use by default for making outgoing calls</string>
+ <!-- Title for setting to select SIM call manager account -->
+ <string name="sim_call_manager_account">Wi-Fi calling account</string>
<!-- Indication to "ask every time" for accounts when making a call -->
<string name="account_ask_every_time">Ask every time</string>
+ <!-- Indication to not use a SIM call manager -->
+ <string name="do_not_use_sim_call_manager">Do not use Wi-Fi calling</string>
+
<!-- DO NOT TRANSLATE. Label for test Subscription 0. -->
<string name="test_account_0_label">Q Mobile</string>
<!-- DO NOT TRANSLATE. Label for test Subscription 1. -->
diff --git a/res/xml/phone_account_preferences.xml b/res/xml/phone_account_preferences.xml
index 716afb2..23313a1 100644
--- a/res/xml/phone_account_preferences.xml
+++ b/res/xml/phone_account_preferences.xml
@@ -18,10 +18,14 @@
for making outgoing phone calls -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/phone_account_preferences_title">
- <ListPreference
- android:key="default_outgoing_account"
- android:title="@string/default_outgoing_account_title"
- android:summary="@string/default_outgoing_account_summary"
- android:defaultValue=""
- android:persistent="false" />
+ <com.android.telecomm.AccountSelectionPreference
+ android:key="default_outgoing_account"
+ android:title="@string/default_outgoing_account_title"
+ android:defaultValue=""
+ android:persistent="false" />
+ <com.android.telecomm.AccountSelectionPreference
+ android:key="sim_call_manager_account"
+ android:title="@string/sim_call_manager_account"
+ android:defaultValue=""
+ android:persistent="false" />
</PreferenceScreen>
diff --git a/src/com/android/telecomm/AccountSelectionPreference.java b/src/com/android/telecomm/AccountSelectionPreference.java
new file mode 100644
index 0000000..e7c2bc0
--- /dev/null
+++ b/src/com/android/telecomm/AccountSelectionPreference.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 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.telecomm;
+
+import android.content.Context;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.telecomm.PhoneAccountHandle;
+import android.util.AttributeSet;
+
+import java.util.List;
+import java.util.Objects;
+
+public class AccountSelectionPreference extends ListPreference implements
+ Preference.OnPreferenceChangeListener {
+
+ public interface AccountSelectionListener {
+ boolean onAccountSelected(AccountSelectionPreference pref, PhoneAccountHandle account);
+ }
+
+ private AccountSelectionListener mListener;
+ private PhoneAccountHandle[] mAccounts;
+ private String[] mEntryValues;
+ private CharSequence[] mEntries;
+
+ public AccountSelectionPreference(Context context) {
+ super(context);
+ setOnPreferenceChangeListener(this);
+ }
+
+ public AccountSelectionPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setOnPreferenceChangeListener(this);
+ }
+
+ public void setListener(AccountSelectionListener listener) {
+ mListener = listener;
+ }
+
+ public void setModel(
+ PhoneAccountRegistrar registrar,
+ List<PhoneAccountHandle> accountsList,
+ PhoneAccountHandle currentSelection,
+ CharSequence nullSelectionString) {
+ mAccounts = accountsList.toArray(new PhoneAccountHandle[accountsList.size()]);
+ mEntryValues = new String[mAccounts.length + 1];
+ mEntries = new CharSequence[mAccounts.length + 1];
+
+ int selectedIndex = mAccounts.length; // Points to nullSelectionString by default
+ int i = 0;
+ for ( ; i < mAccounts.length; i++) {
+ CharSequence label = registrar.getPhoneAccount(mAccounts[i]).getLabel();
+ mEntries[i] = label == null ? null : label.toString();
+ mEntryValues[i] = Integer.toString(i);
+ if (Objects.equals(currentSelection, mAccounts[i])) {
+ selectedIndex = i;
+ }
+ }
+ mEntryValues[i] = Integer.toString(i);
+ mEntries[i] = nullSelectionString;
+
+ setEntryValues(mEntryValues);
+ setEntries(mEntries);
+ setValueIndex(selectedIndex);
+ setSummary(mEntries[selectedIndex]);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mListener != null) {
+ int index = Integer.parseInt((String) newValue);
+ PhoneAccountHandle account = index < mAccounts.length ? mAccounts[index] : null;
+ if (mListener.onAccountSelected(this, account)) {
+ setSummary(mEntries[index]);
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index b434ac3..d76e314 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -35,7 +35,7 @@
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
-import com.android.internal.telecomm.ICallVideoProvider;
+import com.android.internal.telecomm.IVideoCallProvider;
import com.android.internal.telephony.CallerInfo;
import com.android.internal.telephony.CallerInfoAsyncQuery;
import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
@@ -74,7 +74,7 @@
void onParentChanged(Call call);
void onChildrenChanged(Call call);
void onCannedSmsResponsesLoaded(Call call);
- void onCallVideoProviderChanged(Call call);
+ void onVideoCallProviderChanged(Call call);
void onCallerInfoChanged(Call call);
void onAudioModeIsVoipChanged(Call call);
void onStatusHintsChanged(Call call);
@@ -113,7 +113,7 @@
@Override
public void onCannedSmsResponsesLoaded(Call call) {}
@Override
- public void onCallVideoProviderChanged(Call call) {}
+ public void onVideoCallProviderChanged(Call call) {}
@Override
public void onCallerInfoChanged(Call call) {}
@Override
@@ -260,7 +260,7 @@
/** Whether an attempt has been made to load the text message responses. */
private boolean mCannedSmsResponsesLoadingStarted = false;
- private ICallVideoProvider mCallVideoProvider;
+ private IVideoCallProvider mVideoCallProvider;
private boolean mAudioModeIsVoip;
private StatusHints mStatusHints;
@@ -577,7 +577,8 @@
setHandle(connection.getHandle(), connection.getHandlePresentation());
setCallerDisplayName(
connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
- setCallVideoProvider(connection.getCallVideoProvider());
+
+ setVideoCallProvider(connection.getVideoCallProvider());
setVideoState(connection.getVideoState());
if (mIsIncoming) {
@@ -1060,20 +1061,20 @@
}
/**
- * Sets a call video provider for the call.
+ * Sets a video call provider for the call.
*/
- public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
- mCallVideoProvider = callVideoProvider;
+ public void setVideoCallProvider(IVideoCallProvider videoCallProvider) {
+ mVideoCallProvider = videoCallProvider;
for (Listener l : mListeners) {
- l.onCallVideoProviderChanged(Call.this);
+ l.onVideoCallProviderChanged(Call.this);
}
}
/**
- * @return Return the call video Provider binder.
+ * @return Return the {@link VideoCallProvider} binder.
*/
- public ICallVideoProvider getCallVideoProvider() {
- return mCallVideoProvider;
+ public IVideoCallProvider getVideoCallProvider() {
+ return mVideoCallProvider;
}
/**
diff --git a/src/com/android/telecomm/ConnectionServiceWrapper.java b/src/com/android/telecomm/ConnectionServiceWrapper.java
index 00f7124..4238d61 100644
--- a/src/com/android/telecomm/ConnectionServiceWrapper.java
+++ b/src/com/android/telecomm/ConnectionServiceWrapper.java
@@ -33,9 +33,9 @@
import android.telephony.DisconnectCause;
import com.android.internal.os.SomeArgs;
-import com.android.internal.telecomm.ICallVideoProvider;
import com.android.internal.telecomm.IConnectionService;
import com.android.internal.telecomm.IConnectionServiceAdapter;
+import com.android.internal.telecomm.IVideoCallProvider;
import com.android.internal.telecomm.RemoteServiceCallback;
import com.google.common.base.Preconditions;
@@ -250,9 +250,9 @@
SomeArgs args = (SomeArgs) msg.obj;
try {
call = mCallIdMapper.getCall(args.arg1);
- ICallVideoProvider callVideoProvider = (ICallVideoProvider) args.arg2;
+ IVideoCallProvider videoCallProvider = (IVideoCallProvider) args.arg2;
if (call != null) {
- call.setCallVideoProvider(callVideoProvider);
+ call.setVideoCallProvider(videoCallProvider);
}
} finally {
args.recycle();
@@ -373,12 +373,12 @@
}
@Override
- public void setCallVideoProvider(String callId, ICallVideoProvider callVideoProvider) {
- logIncoming("setCallVideoProvider %s", callId);
+ public void setVideoCallProvider(String callId, IVideoCallProvider videoCallProvider) {
+ logIncoming("setVideoCallProvider %s", callId);
mCallIdMapper.checkValidCallId(callId);
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
- args.arg2 = callVideoProvider;
+ args.arg2 = videoCallProvider;
mHandler.obtainMessage(MSG_SET_CALL_VIDEO_PROVIDER, args).sendToTarget();
}
diff --git a/src/com/android/telecomm/CreateConnectionProcessor.java b/src/com/android/telecomm/CreateConnectionProcessor.java
index a0cb688..0f28974 100644
--- a/src/com/android/telecomm/CreateConnectionProcessor.java
+++ b/src/com/android/telecomm/CreateConnectionProcessor.java
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
/**
* This class creates connections to place new outgoing calls to attached to an existing incoming
@@ -33,10 +34,35 @@
* - a connection service cancels the process, in which case the call is aborted
*/
final class CreateConnectionProcessor {
+
+ // Describes information required to attempt to make a phone call
+ private static class CallAttemptRecord {
+ // The PhoneAccount describing the target connection service which we will
+ // contact in order to process an attempt
+ public final PhoneAccountHandle targetConnectionServiceAccount;
+ // The PhoneAccount which we will tell the target connection service to use
+ // for attempting to make the actual phone call
+ public final PhoneAccountHandle phoneAccount;
+
+ public CallAttemptRecord(
+ PhoneAccountHandle targetConnectionServiceAccount,
+ PhoneAccountHandle phoneAccount) {
+ this.targetConnectionServiceAccount = targetConnectionServiceAccount;
+ this.phoneAccount = phoneAccount;
+ }
+
+ @Override
+ public String toString() {
+ return "CallAttemptRecord("
+ + Objects.toString(targetConnectionServiceAccount) + ","
+ + Objects.toString(phoneAccount) + ")";
+ }
+ }
+
private final Call mCall;
private final ConnectionServiceRepository mRepository;
- private List<PhoneAccountHandle> mPhoneAccountHandles;
- private Iterator<PhoneAccountHandle> mPhoneAccountHandleIterator;
+ private List<CallAttemptRecord> mAttemptRecords;
+ private Iterator<CallAttemptRecord> mAttemptRecordIterator;
private CreateConnectionResponse mResponse;
private int mLastErrorCode = DisconnectCause.OUTGOING_FAILURE;
private String mLastErrorMsg;
@@ -50,12 +76,14 @@
void process() {
Log.v(this, "process");
- mPhoneAccountHandles = new ArrayList<>();
+ mAttemptRecords = new ArrayList<>();
if (mCall.getPhoneAccount() != null) {
- mPhoneAccountHandles.add(mCall.getPhoneAccount());
+ mAttemptRecords.add(
+ new CallAttemptRecord(mCall.getPhoneAccount(), mCall.getPhoneAccount()));
}
- adjustPhoneAccountsForEmergency();
- mPhoneAccountHandleIterator = mPhoneAccountHandles.iterator();
+ adjustAttemptsForWifi();
+ adjustAttemptsForEmergency();
+ mAttemptRecordIterator = mAttemptRecords.iterator();
attemptNextPhoneAccount();
}
@@ -80,16 +108,16 @@
private void attemptNextPhoneAccount() {
Log.v(this, "attemptNextPhoneAccount");
- if (mResponse != null && mPhoneAccountHandleIterator.hasNext()) {
- PhoneAccountHandle accountHandle = mPhoneAccountHandleIterator.next();
- Log.i(this, "Trying accountHandle %s", accountHandle);
+ if (mResponse != null && mAttemptRecordIterator.hasNext()) {
+ CallAttemptRecord attempt = mAttemptRecordIterator.next();
+ Log.i(this, "Trying attempt %s", attempt);
ConnectionServiceWrapper service =
- mRepository.getService(accountHandle.getComponentName());
+ mRepository.getService(attempt.targetConnectionServiceAccount.getComponentName());
if (service == null) {
- Log.i(this, "Found no connection service for accountHandle %s", accountHandle);
+ Log.i(this, "Found no connection service for attempt %s", attempt);
attemptNextPhoneAccount();
} else {
- mCall.setPhoneAccount(accountHandle);
+ mCall.setPhoneAccount(attempt.phoneAccount);
mCall.setConnectionService(service);
Log.i(this, "Attempting to call from %s", service.getComponentName());
service.createConnection(mCall, new Response(service));
@@ -104,19 +132,45 @@
}
}
+ // If there exists a registered Wi-Fi calling service, use it.
+ private void adjustAttemptsForWifi() {
+ switch (mAttemptRecords.size()) {
+ case 0:
+ return;
+ case 1:
+ break;
+ default:
+ Log.d(this, "Unexpectedly have > 1 attempt: %s", mAttemptRecords);
+ return;
+ }
+ PhoneAccountHandle simCallManager =
+ TelecommApp.getInstance().getPhoneAccountRegistrar().getSimCallManager();
+ if (simCallManager != null &&
+ !Objects.equals(simCallManager, mAttemptRecords.get(0).phoneAccount)) {
+ mAttemptRecords.set(
+ 0,
+ new CallAttemptRecord(
+ simCallManager,
+ mAttemptRecords.get(0).phoneAccount));
+ }
+ }
+
// If we are possibly attempting to call a local emergency number, ensure that the
// plain PSTN connection services are listed, and nothing else.
- private void adjustPhoneAccountsForEmergency() {
+ private void adjustAttemptsForEmergency() {
if (TelephonyUtil.shouldProcessAsEmergency(TelecommApp.getInstance(), mCall.getHandle())) {
Log.i(this, "Emergency number detected");
- mPhoneAccountHandles.clear();
+ mAttemptRecords.clear();
List<PhoneAccountHandle> allAccountHandles = TelecommApp.getInstance()
.getPhoneAccountRegistrar().getEnabledPhoneAccounts();
for (int i = 0; i < allAccountHandles.size(); i++) {
if (TelephonyUtil.isPstnComponentName(
allAccountHandles.get(i).getComponentName())) {
Log.i(this, "Will try PSTN account %s for emergency", allAccountHandles.get(i));
- mPhoneAccountHandles.add(allAccountHandles.get(i));
+ mAttemptRecords.add(
+ new CallAttemptRecord(
+ allAccountHandles.get(i),
+ allAccountHandles.get(i)));
}
}
}
diff --git a/src/com/android/telecomm/InCallController.java b/src/com/android/telecomm/InCallController.java
index 8442597..10ea443 100644
--- a/src/com/android/telecomm/InCallController.java
+++ b/src/com/android/telecomm/InCallController.java
@@ -73,7 +73,7 @@
}
@Override
- public void onCallVideoProviderChanged(Call call) {
+ public void onVideoCallProviderChanged(Call call) {
updateCall(call);
}
@@ -350,7 +350,7 @@
call.getCallerDisplayNamePresentation(),
call.getGatewayInfo(),
call.getPhoneAccount(),
- call.getCallVideoProvider(),
+ call.getVideoCallProvider(),
parentCallId,
childCallIds,
call.getStatusHints(),
diff --git a/src/com/android/telecomm/PhoneAccountPreferencesActivity.java b/src/com/android/telecomm/PhoneAccountPreferencesActivity.java
index e4c62e7..2430f26 100644
--- a/src/com/android/telecomm/PhoneAccountPreferencesActivity.java
+++ b/src/com/android/telecomm/PhoneAccountPreferencesActivity.java
@@ -18,19 +18,17 @@
import android.app.Activity;
import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
import android.preference.PreferenceFragment;
+import android.telecomm.PhoneAccount;
import android.telecomm.PhoneAccountHandle;
-import java.util.HashMap;
+import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
-import java.util.Objects;
public class PhoneAccountPreferencesActivity extends Activity {
private static final String KEY_DEFAULT_OUTGOING_ACCOUNT = "default_outgoing_account";
+ private static final String KEY_SIM_CALL_MANAGER_ACCOUNT = "sim_call_manager_account";
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -39,54 +37,63 @@
}
public static class PreferencesFragment extends PreferenceFragment
- implements ListPreference.OnPreferenceChangeListener {
- private ListPreference mDefaultOutgoingAccount;
- private PhoneAccountRegistrar mRegistrar;
- private Map<String, PhoneAccountHandle> mAccountByValue = new HashMap<>();
+ implements AccountSelectionPreference.AccountSelectionListener {
+ private AccountSelectionPreference mDefaultOutgoingAccount;
+ private AccountSelectionPreference mSimCallManagerAccount;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.phone_account_preferences);
- mDefaultOutgoingAccount = (ListPreference) findPreference(KEY_DEFAULT_OUTGOING_ACCOUNT);
- mRegistrar = TelecommApp.getInstance().getPhoneAccountRegistrar();
- List<PhoneAccountHandle> accountHandles = mRegistrar.getEnabledPhoneAccounts();
- PhoneAccountHandle currentDefault = mRegistrar.getDefaultOutgoingPhoneAccount();
+ mDefaultOutgoingAccount = (AccountSelectionPreference)
+ findPreference(KEY_DEFAULT_OUTGOING_ACCOUNT);
+ mSimCallManagerAccount = (AccountSelectionPreference)
+ findPreference(KEY_SIM_CALL_MANAGER_ACCOUNT);
- String[] entryValues = new String[accountHandles.size() + 1];
- String[] entries = new String[accountHandles.size() + 1];
+ PhoneAccountRegistrar registrar = TelecommApp.getInstance().getPhoneAccountRegistrar();
- int selectedIndex = accountHandles.size(); // Points to "ask every time" by default
- int i = 0;
- for ( ; i < accountHandles.size(); i++) {
- CharSequence label = mRegistrar.getPhoneAccount(accountHandles.get(i))
- .getLabel();
- entries[i] = label == null ? null : label.toString();
- entryValues[i] = Integer.toString(i);
- if (Objects.equals(currentDefault, accountHandles.get(i))) {
- selectedIndex = i;
- }
- mAccountByValue.put(entryValues[i], accountHandles.get(i));
- }
- entryValues[i] = Integer.toString(i);
- entries[i] = getString(R.string.account_ask_every_time);
- mAccountByValue.put(entryValues[i], null);
+ mDefaultOutgoingAccount.setModel(
+ registrar,
+ registrar.getEnabledPhoneAccounts(),
+ registrar.getDefaultOutgoingPhoneAccount(),
+ getString(R.string.account_ask_every_time));
- mDefaultOutgoingAccount.setEntryValues(entryValues);
- mDefaultOutgoingAccount.setEntries(entries);
- mDefaultOutgoingAccount.setValueIndex(selectedIndex);
- mDefaultOutgoingAccount.setOnPreferenceChangeListener(this);
+ mSimCallManagerAccount.setModel(
+ registrar,
+ getSimCallManagers(registrar),
+ registrar.getSimCallManager(),
+ getString(R.string.do_not_use_sim_call_manager));
+
+ mDefaultOutgoingAccount.setListener(this);
+ mSimCallManagerAccount.setListener(this);
}
@Override
- public boolean onPreferenceChange(Preference p, Object o) {
+ public boolean onAccountSelected(
+ AccountSelectionPreference p, PhoneAccountHandle account) {
+ PhoneAccountRegistrar registrar = TelecommApp.getInstance().getPhoneAccountRegistrar();
if (p == mDefaultOutgoingAccount) {
- mRegistrar.setDefaultOutgoingPhoneAccount(mAccountByValue.get(o));
+ registrar.setDefaultOutgoingPhoneAccount(account);
+ return true;
+ } else if (p == mSimCallManagerAccount) {
+ registrar.setSimCallManager(account);
return true;
}
return false;
}
+
+ private List<PhoneAccountHandle> getSimCallManagers(PhoneAccountRegistrar registrar) {
+ List<PhoneAccountHandle> simCallManagers = new ArrayList<>();
+ List<PhoneAccountHandle> allAccounts = registrar.getAllPhoneAccountHandles();
+ for (int i = 0; i < allAccounts.size(); i++) {
+ PhoneAccount account = registrar.getPhoneAccount(allAccounts.get(i));
+ if ((account.getCapabilities() & PhoneAccount.CAPABILITY_SIM_CALL_MANAGER) != 0) {
+ simCallManagers.add(allAccounts.get(i));
+ }
+ }
+ return simCallManagers;
+ }
}
}
diff --git a/src/com/android/telecomm/PhoneAccountRegistrar.java b/src/com/android/telecomm/PhoneAccountRegistrar.java
index 4c781de..f236f58 100644
--- a/src/com/android/telecomm/PhoneAccountRegistrar.java
+++ b/src/com/android/telecomm/PhoneAccountRegistrar.java
@@ -49,50 +49,47 @@
private static final String PREFERENCE_PHONE_ACCOUNTS = "phone_accounts";
private final Context mContext;
+ private final State mState;
PhoneAccountRegistrar(Context context) {
mContext = context;
+ mState = readState();
}
public PhoneAccountHandle getDefaultOutgoingPhoneAccount() {
- State s = read();
-
- if (s.defaultOutgoingHandle != null) {
+ if (mState.defaultOutgoing != null) {
// Return the registered outgoing default iff it still exists (we keep a sticky
// default to survive account deletion and re-addition)
- for (int i = 0; i < s.accounts.size(); i++) {
- if (s.accounts.get(i).getAccountHandle().equals(s.defaultOutgoingHandle)) {
- return s.defaultOutgoingHandle;
+ for (int i = 0; i < mState.accounts.size(); i++) {
+ if (mState.accounts.get(i).getAccountHandle().equals(mState.defaultOutgoing)) {
+ return mState.defaultOutgoing;
}
}
- // At this point, there was a registered default but it has been deleted; remember
- // it for the future, but return null from this method
- return null;
- } else {
- List<PhoneAccountHandle> enabled = getEnabledPhoneAccounts();
- switch (enabled.size()) {
- case 0:
- // There are no accounts, so there can be no default
- return null;
- case 1:
- // There is only one account, which is by definition the default
- return enabled.get(0);
- default:
- // There are multiple accounts with no selected default
- return null;
- }
+ // At this point, there was a registered default but it has been deleted; proceed
+ // as though there were no default
+ }
+
+ List<PhoneAccountHandle> enabled = getEnabledPhoneAccounts();
+ switch (enabled.size()) {
+ case 0:
+ // There are no accounts, so there can be no default
+ return null;
+ case 1:
+ // There is only one account, which is by definition the default
+ return enabled.get(0);
+ default:
+ // There are multiple accounts with no selected default
+ return null;
}
}
public void setDefaultOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
- State s = read();
-
if (accountHandle == null) {
// Asking to clear the default outgoing is a valid request
- s.defaultOutgoingHandle = null;
+ mState.defaultOutgoing = null;
} else {
boolean found = false;
- for (PhoneAccount m : s.accounts) {
+ for (PhoneAccount m : mState.accounts) {
if (Objects.equals(accountHandle, m.getAccountHandle())) {
found = true;
break;
@@ -105,21 +102,51 @@
return;
}
- s.defaultOutgoingHandle = accountHandle;
+ mState.defaultOutgoing = accountHandle;
}
- write(s);
+ write();
}
+ public void setSimCallManager(PhoneAccountHandle callManager) {
+ if (callManager != null) {
+ PhoneAccount callManagerAccount = getPhoneAccount(callManager);
+ if (callManagerAccount == null) {
+ Log.d(this, "setSimCallManager: Nonexistent call manager: %s", callManager);
+ return;
+ } else if (!has(callManagerAccount, PhoneAccount.CAPABILITY_SIM_CALL_MANAGER)) {
+ Log.d(this, "setSimCallManager: Not a call manager: %s", callManagerAccount);
+ return;
+ }
+ }
+ mState.simCallManager = callManager;
+ write();
+ }
+
+ public PhoneAccountHandle getSimCallManager() {
+ return mState.simCallManager;
+ }
+
+ public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
+ List<PhoneAccountHandle> accountHandles = new ArrayList<>();
+ for (PhoneAccount m : mState.accounts) {
+ accountHandles.add(m.getAccountHandle());
+ }
+ return accountHandles;
+ }
+
+ public List<PhoneAccount> getAllPhoneAccounts() {
+ return new ArrayList<>(mState.accounts);
+ }
+
+ // TODO: Rename systemwide to "getCallProviderPhoneAccounts"?
public List<PhoneAccountHandle> getEnabledPhoneAccounts() {
- State s = read();
- return simSubscriptionAccountHandles(s);
+ return getCallProviderAccountHandles();
}
- public PhoneAccount getPhoneAccount(PhoneAccountHandle accountHandle) {
- State s = read();
- for (PhoneAccount m : s.accounts) {
- if (Objects.equals(accountHandle, m.getAccountHandle())) {
+ public PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
+ for (PhoneAccount m : mState.accounts) {
+ if (Objects.equals(handle, m.getAccountHandle())) {
return m;
}
}
@@ -128,60 +155,112 @@
// TODO: Should we implement an artificial limit for # of accounts associated with a single
// ComponentName?
- public void registerPhoneAccount(PhoneAccount metadata) {
- State s = read();
-
- s.accounts.add(metadata);
+ public void registerPhoneAccount(PhoneAccount account) {
+ account = hackFixBabelAccount(account);
+ mState.accounts.add(account);
// Search for duplicates and remove any that are found.
- for (int i = 0; i < s.accounts.size() - 1; i++) {
- if (Objects.equals(metadata.getAccountHandle(), s.accounts.get(i).getAccountHandle())) {
+ for (int i = 0; i < mState.accounts.size() - 1; i++) {
+ if (Objects.equals(
+ account.getAccountHandle(), mState.accounts.get(i).getAccountHandle())) {
// replace existing entry.
- s.accounts.remove(i);
+ mState.accounts.remove(i);
break;
}
}
- write(s);
+ write();
+ }
+
+ // STOPSHIP: Hack to edit the account registered by Babel so it shows up properly
+ private PhoneAccount hackFixBabelAccount(PhoneAccount account) {
+ String pkg = account.getAccountHandle().getComponentName().getPackageName();
+ return "com.google.android.talk".equals(pkg)
+ ? new PhoneAccount(
+ account.getAccountHandle(),
+ account.getHandle(),
+ account.getSubscriptionNumber(),
+ PhoneAccount.CAPABILITY_SIM_CALL_MANAGER,
+ account.getIconResId(),
+ account.getLabel(),
+ account.getShortDescription(),
+ account.isVideoCallingSupported())
+ : account;
}
public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
- State s = read();
-
- for (int i = 0; i < s.accounts.size(); i++) {
- if (Objects.equals(accountHandle, s.accounts.get(i).getAccountHandle())) {
- s.accounts.remove(i);
+ for (int i = 0; i < mState.accounts.size(); i++) {
+ if (Objects.equals(accountHandle, mState.accounts.get(i).getAccountHandle())) {
+ mState.accounts.remove(i);
break;
}
}
- write(s);
+ write();
}
public void clearAccounts(String packageName) {
- State s = read();
-
- for (int i = 0; i < s.accounts.size(); i++) {
+ for (int i = 0; i < mState.accounts.size(); i++) {
if (Objects.equals(
packageName,
- s.accounts.get(i).getAccountHandle().getComponentName().getPackageName())) {
- s.accounts.remove(i);
+ mState.accounts.get(i).getAccountHandle()
+ .getComponentName().getPackageName())) {
+ mState.accounts.remove(i);
}
}
- write(s);
+ write();
}
- private List<PhoneAccountHandle> simSubscriptionAccountHandles(State s) {
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ // TODO: Add a corresponding has(...) method to class PhoneAccount itself and remove this one
+ // Return true iff the given account has all the specified capability flags
+ static boolean has(PhoneAccount account, int capability) {
+ return (account.getCapabilities() & capability) == capability;
+ }
+
+ private List<PhoneAccountHandle> getCallProviderAccountHandles() {
List<PhoneAccountHandle> accountHandles = new ArrayList<>();
- for (PhoneAccount m : s.accounts) {
- if ((m.getCapabilities() & PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0) {
+ for (PhoneAccount m : mState.accounts) {
+ if (has(m, PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
accountHandles.add(m.getAccountHandle());
}
}
return accountHandles;
}
- private State read() {
+ /**
+ * The state of this {@code PhoneAccountRegistrar}.
+ */
+ private static class State {
+ /**
+ * The account selected by the user to be employed by default for making outgoing calls.
+ * If the user has not made such a selection, then this is null.
+ */
+ public PhoneAccountHandle defaultOutgoing = null;
+
+ /**
+ * A {@code PhoneAccount} having {@link PhoneAccount#CAPABILITY_SIM_CALL_MANAGER} which
+ * manages and optimizes a user's PSTN SIM connections.
+ */
+ public PhoneAccountHandle simCallManager;
+
+ /**
+ * The complete list of {@code PhoneAccount}s known to the Telecomm subsystem.
+ */
+ public final List<PhoneAccount> accounts = new ArrayList<>();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // State management
+ //
+
+ private void write() {
+ writeState(mState);
+ }
+
+ private State readState() {
try {
String serialized = getPreferences().getString(PREFERENCE_PHONE_ACCOUNTS, null);
Log.v(this, "read() obtained serialized state: %s", serialized);
@@ -196,7 +275,7 @@
}
}
- private boolean write(State state) {
+ private boolean writeState(State state) {
try {
Log.v(this, "write() writing state: %s", state);
String serialized = serializeState(state);
@@ -226,11 +305,7 @@
return sStateJson.fromJson(new JSONObject(new JSONTokener(s)));
}
- private static class State {
- public PhoneAccountHandle defaultOutgoingHandle = null;
- public final List<PhoneAccount> accounts = new ArrayList<>();
- }
-
+ ////////////////////////////////////////////////////////////////////////////////////////////////
//
// JSON serialization
//
@@ -243,17 +318,21 @@
private static final Json<State> sStateJson =
new Json<State>() {
private static final String DEFAULT_OUTGOING = "default_outgoing";
+ private static final String SIM_CALL_MANAGER = "sim_call_manager";
private static final String ACCOUNTS = "accounts";
@Override
public JSONObject toJson(State o) throws JSONException {
JSONObject json = new JSONObject();
- if (o.defaultOutgoingHandle != null) {
- json.put(DEFAULT_OUTGOING, sPhoneAccountJson.toJson(o.defaultOutgoingHandle));
+ if (o.defaultOutgoing != null) {
+ json.put(DEFAULT_OUTGOING, sPhoneAccountHandleJson.toJson(o.defaultOutgoing));
+ }
+ if (o.simCallManager != null) {
+ json.put(SIM_CALL_MANAGER, sPhoneAccountHandleJson.toJson(o.simCallManager));
}
JSONArray accounts = new JSONArray();
for (PhoneAccount m : o.accounts) {
- accounts.put(sPhoneAccountMetadataJson.toJson(m));
+ accounts.put(sPhoneAccountJson.toJson(m));
}
json.put(ACCOUNTS, accounts);
return json;
@@ -263,14 +342,26 @@
public State fromJson(JSONObject json) throws JSONException {
State s = new State();
if (json.has(DEFAULT_OUTGOING)) {
- s.defaultOutgoingHandle = sPhoneAccountJson.fromJson(
- (JSONObject) json.get(DEFAULT_OUTGOING));
+ try {
+ s.defaultOutgoing = sPhoneAccountHandleJson.fromJson(
+ (JSONObject) json.get(DEFAULT_OUTGOING));
+ } catch (Exception e) {
+ Log.e(this, e, "Extracting PhoneAccountHandle");
+ }
+ }
+ if (json.has(SIM_CALL_MANAGER)) {
+ try {
+ s.simCallManager = sPhoneAccountHandleJson.fromJson(
+ (JSONObject) json.get(SIM_CALL_MANAGER));
+ } catch (Exception e) {
+ Log.e(this, e, "Extracting PhoneAccountHandle");
+ }
}
if (json.has(ACCOUNTS)) {
JSONArray accounts = (JSONArray) json.get(ACCOUNTS);
for (int i = 0; i < accounts.length(); i++) {
try {
- s.accounts.add(sPhoneAccountMetadataJson.fromJson(
+ s.accounts.add(sPhoneAccountJson.fromJson(
(JSONObject) accounts.get(i)));
} catch (Exception e) {
Log.e(this, e, "Extracting phone account");
@@ -281,7 +372,7 @@
}
};
- private static final Json<PhoneAccount> sPhoneAccountMetadataJson =
+ private static final Json<PhoneAccount> sPhoneAccountJson =
new Json<PhoneAccount>() {
private static final String ACCOUNT = "account";
private static final String HANDLE = "handle";
@@ -295,7 +386,7 @@
@Override
public JSONObject toJson(PhoneAccount o) throws JSONException {
return new JSONObject()
- .put(ACCOUNT, sPhoneAccountJson.toJson(o.getAccountHandle()))
+ .put(ACCOUNT, sPhoneAccountHandleJson.toJson(o.getAccountHandle()))
.put(HANDLE, o.getHandle().toString())
.put(SUBSCRIPTION_NUMBER, o.getSubscriptionNumber())
.put(CAPABILITIES, o.getCapabilities())
@@ -308,7 +399,7 @@
@Override
public PhoneAccount fromJson(JSONObject json) throws JSONException {
return new PhoneAccount(
- sPhoneAccountJson.fromJson((JSONObject) json.get(ACCOUNT)),
+ sPhoneAccountHandleJson.fromJson((JSONObject) json.get(ACCOUNT)),
Uri.parse((String) json.get(HANDLE)),
(String) json.get(SUBSCRIPTION_NUMBER),
(int) json.get(CAPABILITIES),
@@ -319,7 +410,7 @@
}
};
- private static final Json<PhoneAccountHandle> sPhoneAccountJson =
+ private static final Json<PhoneAccountHandle> sPhoneAccountHandleJson =
new Json<PhoneAccountHandle>() {
private static final String COMPONENT_NAME = "component_name";
private static final String ID = "id";
diff --git a/src/com/android/telecomm/TelecommServiceImpl.java b/src/com/android/telecomm/TelecommServiceImpl.java
index b0fc709..3598422 100644
--- a/src/com/android/telecomm/TelecommServiceImpl.java
+++ b/src/com/android/telecomm/TelecommServiceImpl.java
@@ -193,7 +193,8 @@
try {
enforceModifyPermissionOrCallingPackage(
account.getAccountHandle().getComponentName().getPackageName());
- if ((account.getCapabilities() & PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0) {
+ if (PhoneAccountRegistrar.has(account, PhoneAccount.CAPABILITY_CALL_PROVIDER) ||
+ PhoneAccountRegistrar.has(account, PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
enforceModifyPermissionOrCallingPackage(TELEPHONY_PACKAGE_NAME);
}
mPhoneAccountRegistrar.registerPhoneAccount(account);
diff --git a/tests/src/com/android/telecomm/testapps/TestConnectionService.java b/tests/src/com/android/telecomm/testapps/TestConnectionService.java
index 72960e5..baac4e1 100644
--- a/tests/src/com/android/telecomm/testapps/TestConnectionService.java
+++ b/tests/src/com/android/telecomm/testapps/TestConnectionService.java
@@ -138,7 +138,7 @@
private final boolean mIsIncoming;
/** Used to cleanup camera and media when done with connection. */
- private TestCallVideoProvider mTestCallVideoProvider;
+ private TestVideoCallProvider mTestVideoCallProvider;
TestConnection(boolean isIncoming) {
mIsIncoming = isIncoming;
@@ -259,17 +259,17 @@
}
}
- public void setTestCallVideoProvider(TestCallVideoProvider testCallVideoProvider) {
- mTestCallVideoProvider = testCallVideoProvider;
+ public void setTestVideoCallProvider(TestVideoCallProvider testVideoCallProvider) {
+ mTestVideoCallProvider = testVideoCallProvider;
}
/**
* Stops playback of test videos.
*/
private void stopAndCleanupMedia() {
- if (mTestCallVideoProvider != null) {
- mTestCallVideoProvider.stopAndCleanupMedia();
- mTestCallVideoProvider.stopCamera();
+ if (mTestVideoCallProvider != null) {
+ mTestVideoCallProvider.stopAndCleanupMedia();
+ mTestVideoCallProvider.stopCamera();
}
}
}
@@ -440,12 +440,12 @@
// Use dummy number for testing incoming calls.
Uri handle = Uri.fromParts(SCHEME_TEL, getDummyNumber(isVideoCall), null);
if (isVideoCall) {
- TestCallVideoProvider testCallVideoProvider =
- new TestCallVideoProvider(getApplicationContext());
- connection.setCallVideoProvider(testCallVideoProvider);
+ TestVideoCallProvider testVideoCallProvider =
+ new TestVideoCallProvider(getApplicationContext());
+ connection.setVideoCallProvider(testVideoCallProvider);
// Keep reference to original so we can clean up the media players later.
- connection.setTestCallVideoProvider(testCallVideoProvider);
+ connection.setTestVideoCallProvider(testVideoCallProvider);
}
// Assume all calls are video capable.
diff --git a/tests/src/com/android/telecomm/testapps/TestCallVideoProvider.java b/tests/src/com/android/telecomm/testapps/TestVideoCallProvider.java
similarity index 91%
rename from tests/src/com/android/telecomm/testapps/TestCallVideoProvider.java
rename to tests/src/com/android/telecomm/testapps/TestVideoCallProvider.java
index 3f83bb9..ad4814d 100644
--- a/tests/src/com/android/telecomm/testapps/TestCallVideoProvider.java
+++ b/tests/src/com/android/telecomm/testapps/TestVideoCallProvider.java
@@ -31,11 +31,9 @@
import android.hardware.camera2.TotalCaptureResult;
import android.media.MediaPlayer;
import android.os.Handler;
-import android.os.RemoteException;
import android.telecomm.CallCameraCapabilities;
-import android.telecomm.CallVideoClient;
-import android.telecomm.CallVideoProvider;
-import android.telecomm.RemoteCallVideoClient;
+import android.telecomm.InCallService.VideoCall;
+import android.telecomm.VideoCallProvider;
import android.telecomm.VideoCallProfile;
import android.text.TextUtils;
import android.util.Log;
@@ -46,10 +44,9 @@
import java.util.Random;
/**
- * Implements the CallVideoProvider.
+ * Implements the VideoCallProvider.
*/
-public class TestCallVideoProvider extends CallVideoProvider {
- private RemoteCallVideoClient mCallVideoClient;
+public class TestVideoCallProvider extends VideoCallProvider {
private CallCameraCapabilities mCapabilities;
private Random random;
private Surface mDisplaySurface;
@@ -67,21 +64,13 @@
private static final long SESSION_TIMEOUT_MS = 2000;
- public TestCallVideoProvider(Context context) {
+ public TestVideoCallProvider(Context context) {
mContext = context;
mCapabilities = new CallCameraCapabilities(false /* zoomSupported */, 0 /* maxZoom */);
random = new Random();
mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
}
- /**
- * Save the reference to the CallVideoClient so callback can be invoked.
- */
- @Override
- public void onSetCallVideoClient(RemoteCallVideoClient callVideoClient) {
- mCallVideoClient = callVideoClient;
- }
-
@Override
public void onSetCamera(String cameraId) {
log("Set camera to " + cameraId);
@@ -145,7 +134,7 @@
mCapabilities = new CallCameraCapabilities(true /* zoomSupported */, value);
}
- mCallVideoClient.handleCameraCapabilitiesChange(mCapabilities);
+ changeCameraCapabilities(mCapabilities);
}
/**
@@ -158,8 +147,8 @@
VideoCallProfile responseProfile = new VideoCallProfile(
requestProfile.getVideoState(), requestProfile.getQuality());
- mCallVideoClient.receiveSessionModifyResponse(
- CallVideoClient.SESSION_MODIFY_REQUEST_SUCCESS,
+ receiveSessionModifyResponse(
+ VideoCall.SESSION_MODIFY_REQUEST_SUCCESS,
requestProfile,
responseProfile);
}
@@ -175,7 +164,7 @@
@Override
public void onRequestCameraCapabilities() {
log("Requested camera capabilities");
- mCallVideoClient.handleCameraCapabilitiesChange(mCapabilities);
+ changeCameraCapabilities(mCapabilities);
}
/**
@@ -185,7 +174,7 @@
public void onRequestCallDataUsage() {
log("Requested call data usage");
int dataUsageKb = (10 *1024) + random.nextInt(50 * 1024);
- mCallVideoClient.updateCallDataUsage(dataUsageKb);
+ changeCallDataUsage(dataUsageKb);
}
/**