Remove Session Id from ImsService APIs that do not need it.
am: b5f30c7854
Change-Id: I064d41d55246dbc5ed4fa131852320070c40dd9d
diff --git a/src/java/com/android/ims/ImsConnectionStateListener.java b/src/java/com/android/ims/ImsConnectionStateListener.java
index 1158375..f281df1 100644
--- a/src/java/com/android/ims/ImsConnectionStateListener.java
+++ b/src/java/com/android/ims/ImsConnectionStateListener.java
@@ -34,6 +34,13 @@
}
/**
+ * Called when the device is connected to the IMS network with {@param imsRadioTech}.
+ */
+ public void onImsConnected(int imsRadioTech) {
+ // no-op
+ }
+
+ /**
* Called when the device is trying to connect to the IMS network.
*/
public void onImsProgressing() {
@@ -83,4 +90,11 @@
public void registrationAssociatedUriChanged(Uri[] uris) {
// no-op
}
+
+ /**
+ * Called when IMS registration attempt on {@param imsRadioTech} failed
+ */
+ public void onRegistrationChangeFailed(int imsRadioTech, ImsReasonInfo imsReasonInfo) {
+ // no-op
+ }
}
diff --git a/src/java/com/android/ims/ImsManager.java b/src/java/com/android/ims/ImsManager.java
index 98ff583..96debae 100644
--- a/src/java/com/android/ims/ImsManager.java
+++ b/src/java/com/android/ims/ImsManager.java
@@ -471,9 +471,12 @@
imsManager.turnOffIms();
}
- // Force IMS to register over LTE when turning off WFC
+ TelephonyManager tm = (TelephonyManager) context
+ .getSystemService(Context.TELEPHONY_SERVICE);
setWfcModeInternal(context, enabled
- ? getWfcMode(context)
+ // Choose wfc mode per current roaming preference
+ ? getWfcMode(context, tm.isNetworkRoaming())
+ // Force IMS to register over LTE when turning off WFC
: ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
} catch (ImsException e) {
loge("setWfcSetting(): ", e);
@@ -821,8 +824,11 @@
boolean enabled = isVtEnabledByUser(mContext);
boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
boolean isDataEnabled = isDataEnabled();
+ boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
+ CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
- boolean isFeatureOn = available && enabled && isNonTty && isDataEnabled;
+ boolean isFeatureOn = available && enabled && isNonTty
+ && (ignoreDataEnabledChanged || isDataEnabled);
log("updateVideoCallFeatureValue: available = " + available
+ ", enabled = " + enabled
@@ -1561,8 +1567,10 @@
TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, mImsConfigListener);
if (isVtEnabledByPlatform(mContext)) {
+ boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
+ CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
boolean enableViLte = turnOn && isVtEnabledByUser(mContext) &&
- isDataEnabled();
+ (ignoreDataEnabledChanged || isDataEnabled());
config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
TelephonyManager.NETWORK_TYPE_LTE,
enableViLte ? 1 : 0,
@@ -1684,6 +1692,7 @@
if (mListener != null) {
mListener.onImsConnected();
+ mListener.onImsConnected(imsRadioTech);
}
}
@@ -1773,6 +1782,16 @@
mListener.registrationAssociatedUriChanged(uris);
}
}
+
+ @Override
+ public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo) {
+ if (DBG) log("registrationChangeFailed :: targetAccessTech=" + targetAccessTech +
+ ", imsReasonInfo=" + imsReasonInfo);
+
+ if (mListener != null) {
+ mListener.onRegistrationChangeFailed(targetAccessTech, imsReasonInfo);
+ }
+ }
}
/**
@@ -1913,6 +1932,8 @@
pw.println(" mConfigUpdated = " + mConfigUpdated);
pw.println(" mImsServiceProxy = " + mImsServiceProxy);
pw.println(" mDataEnabled = " + isDataEnabled());
+ pw.println(" ignoreDataEnabledChanged = " + getBooleanCarrierConfig(mContext,
+ CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS));
pw.println(" isGbaValid = " + isGbaValid(mContext));
pw.println(" isImsTurnOffAllowed = " + isImsTurnOffAllowed());
diff --git a/src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java b/src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java
index 0b239ac..fcb9e09 100644
--- a/src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java
+++ b/src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java
@@ -25,6 +25,7 @@
import android.os.RegistrantList;
import android.os.RemoteException;
import android.telecom.Connection;
+import android.telecom.Log;
import android.telecom.VideoProfile;
import android.view.Surface;
@@ -65,6 +66,7 @@
private RegistrantList mDataUsageUpdateRegistrants = new RegistrantList();
private final Set<ImsVideoProviderWrapperCallback> mCallbacks = Collections.newSetFromMap(
new ConcurrentHashMap<ImsVideoProviderWrapperCallback, Boolean>(8, 0.9f, 1));
+ private VideoPauseTracker mVideoPauseTracker = new VideoPauseTracker();
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
@@ -254,9 +256,29 @@
}
}
- /** @inheritDoc */
+ /**
+ * Handles session modify requests received from the {@link android.telecom.InCallService}.
+ *
+ * @inheritDoc
+ **/
public void onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
+ if (fromProfile == null || toProfile == null) {
+ Log.w(this, "onSendSessionModifyRequest: null profile in request.");
+ return;
+ }
+
try {
+ toProfile = maybeFilterPauseResume(fromProfile, toProfile,
+ VideoPauseTracker.SOURCE_INCALL);
+
+ int fromVideoState = fromProfile.getVideoState();
+ int toVideoState = toProfile.getVideoState();
+ Log.i(this, "onSendSessionModifyRequest: fromVideoState=%s, toVideoState=%s; ",
+ VideoProfile.videoStateToString(fromProfile.getVideoState()),
+ VideoProfile.videoStateToString(toProfile.getVideoState()));
+ if (fromVideoState == toVideoState) {
+ return;
+ }
mVideoCallProvider.sendSessionModifyRequest(fromProfile, toProfile);
} catch (RemoteException e) {
}
@@ -293,4 +315,154 @@
} catch (RemoteException e) {
}
}
+
+ /**
+ * Determines if a session modify request represents a request to pause the video.
+ *
+ * @param from The from video state.
+ * @param to The to video state.
+ * @return {@code true} if a pause was requested.
+ */
+ private static boolean isPauseRequest(int from, int to) {
+ boolean fromPaused = VideoProfile.isPaused(from);
+ boolean toPaused = VideoProfile.isPaused(to);
+
+ return !fromPaused && toPaused;
+ }
+
+ /**
+ * Determines if a session modify request represents a request to resume the video.
+ *
+ * @param from The from video state.
+ * @param to The to video state.
+ * @return {@code true} if a resume was requested.
+ */
+ private static boolean isResumeRequest(int from, int to) {
+ boolean fromPaused = VideoProfile.isPaused(from);
+ boolean toPaused = VideoProfile.isPaused(to);
+
+ return fromPaused && !toPaused;
+ }
+
+ /**
+ * Filters incoming pause and resume requests based on whether there are other active pause or
+ * resume requests at the current time.
+ *
+ * Requests to pause the video stream using the {@link VideoProfile#STATE_PAUSED} bit can come
+ * from both the {@link android.telecom.InCallService}, as well as via the
+ * {@link #pauseVideo(int, int)} and {@link #resumeVideo(int, int)} methods. As a result,
+ * multiple sources can potentially pause or resume the video stream. This method ensures that
+ * providing any one request source has paused the video that the video will remain paused.
+ *
+ * @param fromProfile The request's from {@link VideoProfile}.
+ * @param toProfile The request's to {@link VideoProfile}.
+ * @param source The source of the request, as identified by a {@code VideoPauseTracker#SOURCE*}
+ * constant.
+ * @return The new toProfile, with the pause bit set or unset based on whether we should
+ * actually pause or resume the video at the current time.
+ */
+ private VideoProfile maybeFilterPauseResume(VideoProfile fromProfile, VideoProfile toProfile,
+ int source) {
+ int fromVideoState = fromProfile.getVideoState();
+ int toVideoState = toProfile.getVideoState();
+
+ // TODO: Remove the following workaround in favor of a new API.
+ // The current sendSessionModifyRequest API has a flaw. If the video is already
+ // paused, it is not possible for the IncallService to inform the VideoProvider that
+ // it wishes to pause due to multi-tasking.
+ // In a future release we should add a new explicity pauseVideo and resumeVideo API
+ // instead of a difference between two video states.
+ // For now, we'll assume if the request is from pause to pause, we'll still try to
+ // pause.
+ boolean isPauseSpecialCase = (source == VideoPauseTracker.SOURCE_INCALL &&
+ VideoProfile.isPaused(fromVideoState) &&
+ VideoProfile.isPaused(toVideoState));
+
+ boolean isPauseRequest = isPauseRequest(fromVideoState, toVideoState) || isPauseSpecialCase;
+ boolean isResumeRequest = isResumeRequest(fromVideoState, toVideoState);
+ if (isPauseRequest) {
+ Log.i(this, "maybeFilterPauseResume: isPauseRequest");
+ // Check if we have already paused the video in the past.
+ if (!mVideoPauseTracker.shouldPauseVideoFor(source) && !isPauseSpecialCase) {
+ // Note: We don't want to remove the "pause" in the "special case" scenario. If we
+ // do the resulting request will be from PAUSED --> UNPAUSED, which would resume the
+ // video.
+
+ // Video was already paused, so remove the pause in the "to" profile.
+ toVideoState = toVideoState & ~VideoProfile.STATE_PAUSED;
+ toProfile = new VideoProfile(toVideoState, toProfile.getQuality());
+ }
+ } else if (isResumeRequest) {
+ Log.i(this, "maybeFilterPauseResume: isResumeRequest");
+ // Check if we should remain paused (other pause requests pending).
+ if (!mVideoPauseTracker.shouldResumeVideoFor(source)) {
+ // There are other pause requests from other sources which are still active, so we
+ // should remain paused.
+ toVideoState = toVideoState | VideoProfile.STATE_PAUSED;
+ toProfile = new VideoProfile(toVideoState, toProfile.getQuality());
+ }
+ }
+
+ return toProfile;
+ }
+
+ /**
+ * Issues a request to pause the video using {@link VideoProfile#STATE_PAUSED} from a source
+ * other than the InCall UI.
+ *
+ * @param fromVideoState The current video state (prior to issuing the pause).
+ * @param source The source of the pause request.
+ */
+ public void pauseVideo(int fromVideoState, int source) {
+ if (mVideoPauseTracker.shouldPauseVideoFor(source)) {
+ // We should pause the video (its not already paused).
+ VideoProfile fromProfile = new VideoProfile(fromVideoState);
+ VideoProfile toProfile = new VideoProfile(fromVideoState | VideoProfile.STATE_PAUSED);
+
+ try {
+ Log.i(this, "pauseVideo: fromVideoState=%s, toVideoState=%s",
+ VideoProfile.videoStateToString(fromProfile.getVideoState()),
+ VideoProfile.videoStateToString(toProfile.getVideoState()));
+ mVideoCallProvider.sendSessionModifyRequest(fromProfile, toProfile);
+ } catch (RemoteException e) {
+ }
+ } else {
+ Log.i(this, "pauseVideo: video already paused");
+ }
+ }
+
+ /**
+ * Issues a request to resume the video using {@link VideoProfile#STATE_PAUSED} from a source
+ * other than the InCall UI.
+ *
+ * @param fromVideoState The current video state (prior to issuing the resume).
+ * @param source The source of the resume request.
+ */
+ public void resumeVideo(int fromVideoState, int source) {
+ if (mVideoPauseTracker.shouldResumeVideoFor(source)) {
+ // We are the last source to resume, so resume now.
+ VideoProfile fromProfile = new VideoProfile(fromVideoState);
+ VideoProfile toProfile = new VideoProfile(fromVideoState & ~VideoProfile.STATE_PAUSED);
+
+ try {
+ Log.i(this, "resumeVideo: fromVideoState=%s, toVideoState=%s",
+ VideoProfile.videoStateToString(fromProfile.getVideoState()),
+ VideoProfile.videoStateToString(toProfile.getVideoState()));
+ mVideoCallProvider.sendSessionModifyRequest(fromProfile, toProfile);
+ } catch (RemoteException e) {
+ }
+ } else {
+ Log.i(this, "resumeVideo: remaining paused (paused from other sources)");
+ }
+ }
+
+ /**
+ * Determines if a specified source has issued a pause request.
+ *
+ * @param source The source.
+ * @return {@code true} if the source issued a pause request, {@code false} otherwise.
+ */
+ public boolean wasVideoPausedFromSource(int source) {
+ return mVideoPauseTracker.wasVideoPausedFromSource(source);
+ }
}
diff --git a/src/java/com/android/ims/internal/VideoPauseTracker.java b/src/java/com/android/ims/internal/VideoPauseTracker.java
new file mode 100644
index 0000000..d37f7fa
--- /dev/null
+++ b/src/java/com/android/ims/internal/VideoPauseTracker.java
@@ -0,0 +1,181 @@
+/*
+ * 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.ims.internal;
+
+import android.telecom.Log;
+import android.telecom.VideoProfile;
+import android.util.ArraySet;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+/**
+ * Used by an {@link ImsVideoCallProviderWrapper} to track requests to pause video from various
+ * sources.
+ *
+ * Requests to pause the video stream using the {@link VideoProfile#STATE_PAUSED} bit can come
+ * from both the {@link android.telecom.InCallService}, as well as via the
+ * {@link ImsVideoCallProviderWrapper#pauseVideo(int, int)} and
+ * {@link ImsVideoCallProviderWrapper#resumeVideo(int, int)} methods. As a result, multiple sources
+ * can potentially pause or resume the video stream.
+ *
+ * This class is responsible for tracking any active requests to pause the video.
+ */
+public class VideoPauseTracker {
+ /** The pause or resume request originated from an InCallService. */
+ public static final int SOURCE_INCALL = 1;
+
+ /**
+ * The pause or resume request originated from a change to the data enabled state from the
+ * {@code ImsPhoneCallTracker#onDataEnabledChanged(boolean, int)} callback. This happens when
+ * the user reaches their data limit or enables and disables data.
+ */
+ public static final int SOURCE_DATA_ENABLED = 2;
+
+ private static final String SOURCE_INCALL_STR = "INCALL";
+ private static final String SOURCE_DATA_ENABLED_STR = "DATA_ENABLED";
+
+ /**
+ * Tracks the current sources of pause requests.
+ */
+ private Set<Integer> mPauseRequests = new ArraySet<Integer>(2);
+
+ /**
+ * Lock for the {@link #mPauseRequests} {@link ArraySet}.
+ */
+ private Object mPauseRequestsLock = new Object();
+
+ /**
+ * Tracks a request to pause the video for a source (see {@link #SOURCE_DATA_ENABLED},
+ * {@link #SOURCE_INCALL}) and determines whether a pause request should be issued to the
+ * video provider.
+ *
+ * We want to issue a pause request to the provider when we receive the first request
+ * to pause via any source and we're not already paused.
+ *
+ * @param source The source of the pause request.
+ * @return {@code true} if a pause should be issued to the
+ * {@link com.android.ims.internal.ImsVideoCallProvider}, {@code false} otherwise.
+ */
+ public boolean shouldPauseVideoFor(int source) {
+ synchronized (mPauseRequestsLock) {
+ boolean wasPaused = isPaused();
+ mPauseRequests.add(source);
+
+ if (!wasPaused) {
+ Log.i(this, "shouldPauseVideoFor: source=%s, pendingRequests=%s - should pause",
+ sourceToString(source), sourcesToString(mPauseRequests));
+ // There were previously no pause requests, but there is one now, so pause.
+ return true;
+ } else {
+ Log.i(this, "shouldPauseVideoFor: source=%s, pendingRequests=%s - already paused",
+ sourceToString(source), sourcesToString(mPauseRequests));
+ // There were already pause requests, so no need to re-pause.
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Tracks a request to resume the video for a source (see {@link #SOURCE_DATA_ENABLED},
+ * {@link #SOURCE_INCALL}) and determines whether a resume request should be issued to the
+ * video provider.
+ *
+ * We want to issue a resume request to the provider when we have issued a corresponding
+ * resume for each previously issued pause.
+ *
+ * @param source The source of the resume request.
+ * @return {@code true} if a resume should be issued to the
+ * {@link com.android.ims.internal.ImsVideoCallProvider}, {@code false} otherwise.
+ */
+ public boolean shouldResumeVideoFor(int source) {
+ synchronized (mPauseRequestsLock) {
+ boolean wasPaused = isPaused();
+ mPauseRequests.remove(source);
+ boolean isPaused = isPaused();
+
+ if (wasPaused && !isPaused) {
+ Log.i(this, "shouldResumeVideoFor: source=%s, pendingRequests=%s - should resume",
+ sourceToString(source), sourcesToString(mPauseRequests));
+ // This was the last pause request, so resume video.
+ return true;
+ } else if (wasPaused && isPaused) {
+ Log.i(this, "shouldResumeVideoFor: source=%s, pendingRequests=%s - stay paused",
+ sourceToString(source), sourcesToString(mPauseRequests));
+ // There are still pending pause requests, so don't resume.
+ return false;
+ } else {
+ Log.i(this, "shouldResumeVideoFor: source=%s, pendingRequests=%s - not paused",
+ sourceToString(source), sourcesToString(mPauseRequests));
+ // Video wasn't paused, so don't resume.
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @return {@code true} if the video should be paused, {@code false} otherwise.
+ */
+ public boolean isPaused() {
+ synchronized (mPauseRequestsLock) {
+ return !mPauseRequests.isEmpty();
+ }
+ }
+
+ /**
+ * @param source the source of the pause.
+ * @return {@code true} if the specified source initiated a pause request and the video is
+ * currently paused, {@code false} otherwise.
+ */
+ public boolean wasVideoPausedFromSource(int source) {
+ synchronized (mPauseRequestsLock) {
+ return mPauseRequests.contains(source);
+ }
+ }
+
+ /**
+ * Returns a string equivalent of a {@code SOURCE_*} constant.
+ *
+ * @param source A {@code SOURCE_*} constant.
+ * @return String equivalent of the source.
+ */
+ private String sourceToString(int source) {
+ switch (source) {
+ case SOURCE_DATA_ENABLED:
+ return SOURCE_DATA_ENABLED_STR;
+ case SOURCE_INCALL:
+ return SOURCE_INCALL_STR;
+ }
+ return "unknown";
+ }
+
+ /**
+ * Returns a comma separated list of sources.
+ *
+ * @param sources The sources.
+ * @return Comma separated list of sources.
+ */
+ private String sourcesToString(Collection<Integer> sources) {
+ synchronized (mPauseRequestsLock) {
+ return sources.stream()
+ .map(source -> sourceToString(source))
+ .collect(Collectors.joining(", "));
+ }
+ }
+}