diff options
author | 2022-11-28 06:23:02 +0000 | |
---|---|---|
committer | 2023-01-17 20:43:17 +0000 | |
commit | 8ec56249fb89fde3dca22b19958ba35091e79f29 (patch) | |
tree | b855703cb3b20566f8b03cde3ce2eb19906c6290 | |
parent | ccb92936e092663fab41f2dc0dac429fcf98e14f (diff) |
Location Query for OTT Emergency Calls (frameworks)
This commit implements Location Query for OTT Emergency Calls
in frameworks.
Bug: 236748912
Test: cts test
Change-Id: Id1fbd33e7027146bca9d3a723977d1f63b077696
9 files changed, 299 insertions, 0 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index a8dddcf1e3d0..c5704ff839ae 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -41314,6 +41314,7 @@ package android.telecom { method public void onUsingAlternativeUi(boolean); method public static String propertiesToString(int); method public final void putExtras(@NonNull android.os.Bundle); + method public final void queryLocationForEmergency(@IntRange(from=100, to=5000) long, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.location.Location,android.telecom.QueryLocationException>); method public final void removeExtras(java.util.List<java.lang.String>); method public final void removeExtras(java.lang.String...); method @Deprecated public void requestBluetoothAudio(@NonNull android.bluetooth.BluetoothDevice); @@ -41716,6 +41717,22 @@ package android.telecom { field public static final int REASON_USER_SET = 3; // 0x3 } + public final class QueryLocationException extends java.lang.RuntimeException implements android.os.Parcelable { + ctor public QueryLocationException(@Nullable String); + ctor public QueryLocationException(@Nullable String, int); + ctor public QueryLocationException(@Nullable String, int, @Nullable Throwable); + method public int describeContents(); + method public int getCode(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telecom.QueryLocationException> CREATOR; + field public static final int ERROR_NOT_ALLOWED_FOR_NON_EMERGENCY_CONNECTIONS = 4; // 0x4 + field public static final int ERROR_NOT_PERMITTED = 3; // 0x3 + field public static final int ERROR_PREVIOUS_REQUEST_EXISTS = 2; // 0x2 + field public static final int ERROR_REQUEST_TIME_OUT = 1; // 0x1 + field public static final int ERROR_SERVICE_UNAVAILABLE = 5; // 0x5 + field public static final int ERROR_UNSPECIFIED = 6; // 0x6 + } + public final class RemoteConference { method public void disconnect(); method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections(); diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 465622658f7d..6138f67bfb0f 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -33,6 +33,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Intent; import android.hardware.camera2.CameraManager; +import android.location.Location; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -1050,6 +1051,13 @@ public abstract class Connection extends Conferenceable { public static final String EXTRA_CALL_QUALITY_REPORT = "android.telecom.extra.CALL_QUALITY_REPORT"; + /** + * Key to obtain location as a result of ({@code queryLocationForEmergency} from Bundle + * @hide + */ + public static final String EXTRA_KEY_QUERY_LOCATION = + "android.telecom.extra.KEY_QUERY_LOCATION"; + // Flag controlling whether PII is emitted into the logs private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG); @@ -1285,6 +1293,9 @@ public abstract class Connection extends Conferenceable { public void onConnectionTimeReset(Connection c) {} public void onEndpointChanged(Connection c, CallEndpoint endpoint, Executor executor, OutcomeReceiver<Void, CallEndpointException> callback) {} + public void onQueryLocation(Connection c, long timeoutMillis, @NonNull String provider, + @NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<Location, QueryLocationException> callback) {} } /** @@ -3233,6 +3244,36 @@ public abstract class Connection extends Conferenceable { } /** + * Query the device's location in order to place an Emergency Call. + * Only SIM call managers can call this method for Connections representing Emergency calls. + * If a previous location query request is not completed, the new location query request will + * be rejected and return a QueryLocationException with + * {@code QueryLocationException#ERROR_PREVIOUS_REQUEST_EXISTS} + * + * @param timeoutMillis long: Timeout in millis waiting for query response (MAX:5000, MIN:100). + * @param provider String: the location provider name, This value cannot be null. + * It is the caller's responsibility to select an enabled provider. The caller + * can use {@link android.location.LocationManager#getProviders(boolean)} + * to choose one of the enabled providers and pass it in. + * @param executor The executor of where the callback will execute. + * @param callback The callback to notify the result of queryLocation. + */ + public final void queryLocationForEmergency( + @IntRange(from = 100, to = 5000) long timeoutMillis, + @NonNull String provider, + @NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<Location, QueryLocationException> callback) { + if (provider == null || executor == null || callback == null) { + throw new IllegalArgumentException("There are arguments that must not be null"); + } + if (timeoutMillis < 100 || timeoutMillis > 5000) { + throw new IllegalArgumentException("The timeoutMillis should be min 100, max 5000"); + } + mListeners.forEach((l) -> + l.onQueryLocation(this, timeoutMillis, provider, executor, callback)); + } + + /** * Notifies this Connection that the {@link #getAudioState()} property has a new value. * * @param state The new connection audio state. diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 4d6caf834e89..773ed705a999 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -16,6 +16,7 @@ package android.telecom; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -25,6 +26,7 @@ import android.annotation.TestApi; import android.app.Service; import android.content.ComponentName; import android.content.Intent; +import android.location.Location; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -2020,6 +2022,16 @@ public abstract class ConnectionService extends Service { mAdapter.requestCallEndpointChange(id, endpoint, executor, callback); } } + + @Override + public void onQueryLocation(Connection c, long timeoutMillis, @NonNull String provider, + @NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<Location, QueryLocationException> callback) { + String id = mIdByConnection.get(c); + if (id != null) { + mAdapter.queryLocation(id, timeoutMillis, provider, executor, callback); + } + } }; /** {@inheritDoc} */ diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java index 39928ef526af..a7105d349f26 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java @@ -16,6 +16,9 @@ package android.telecom; +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.location.Location; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -748,4 +751,45 @@ final class ConnectionServiceAdapter implements DeathRecipient { } } } + + /** + * Query location information. + * Only SIM call managers can call this method for Connections representing Emergency calls. + * If the previous request is not completed, the new request will be rejected. + * + * @param timeoutMillis long: Timeout in millis waiting for query response. + * @param provider String: the location provider name, This value cannot be null. + * @param executor The executor of where the callback will execute. + * @param callback The callback to notify the result of queryLocation. + */ + void queryLocation(String callId, long timeoutMillis, @NonNull String provider, + @NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<Location, QueryLocationException> callback) { + Log.v(this, "queryLocation: %s %d", callId, timeoutMillis); + for (IConnectionServiceAdapter adapter : mAdapters) { + try { + adapter.queryLocation(callId, timeoutMillis, provider, + new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle result) { + super.onReceiveResult(resultCode, result); + + if (resultCode == 1 /* success */) { + executor.execute(() -> callback.onResult(result.getParcelable( + Connection.EXTRA_KEY_QUERY_LOCATION, Location.class))); + } else { + executor.execute(() -> callback.onError(result.getParcelable( + QueryLocationException.QUERY_LOCATION_ERROR, + QueryLocationException.class))); + } + } + }, + Log.getExternalSession()); + } catch (RemoteException e) { + Log.d(this, "queryLocation: Exception e : " + e); + executor.execute(() -> callback.onError(new QueryLocationException( + e.getMessage(), QueryLocationException.ERROR_SERVICE_UNAVAILABLE))); + } + } + } } diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java index c95e14f0113a..8a59020fc580 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java @@ -78,6 +78,7 @@ final class ConnectionServiceAdapterServant { private static final int MSG_SET_CONFERENCE_STATE = 36; private static final int MSG_HANDLE_CREATE_CONFERENCE_COMPLETE = 37; private static final int MSG_SET_CALL_DIRECTION = 38; + private static final int MSG_QUERY_LOCATION = 39; private final IConnectionServiceAdapter mDelegate; @@ -373,6 +374,18 @@ final class ConnectionServiceAdapterServant { } finally { args.recycle(); } + break; + } + case MSG_QUERY_LOCATION: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.queryLocation((String) args.arg1, (long) args.arg2, + (String) args.arg3, (ResultReceiver) args.arg4, + (Session.Info) args.arg5); + } finally { + args.recycle(); + } + break; } } } @@ -699,6 +712,18 @@ final class ConnectionServiceAdapterServant { ResultReceiver callback, Session.Info sessionInfo) { // Do nothing } + + @Override + public void queryLocation(String callId, long timeoutMillis, String provider, + ResultReceiver callback, Session.Info sessionInfo) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = timeoutMillis; + args.arg3 = provider; + args.arg4 = callback; + args.arg5 = sessionInfo; + mHandler.obtainMessage(MSG_QUERY_LOCATION, args).sendToTarget(); + } }; public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) { diff --git a/telecomm/java/android/telecom/QueryLocationException.aidl b/telecomm/java/android/telecom/QueryLocationException.aidl new file mode 100644 index 000000000000..56ac4126cac4 --- /dev/null +++ b/telecomm/java/android/telecom/QueryLocationException.aidl @@ -0,0 +1,22 @@ +/* + * Copyright 2022, 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 android.telecom; + +/** + * {@hide} + */ +parcelable QueryLocationException;
\ No newline at end of file diff --git a/telecomm/java/android/telecom/QueryLocationException.java b/telecomm/java/android/telecom/QueryLocationException.java new file mode 100644 index 000000000000..fd90d1ec3572 --- /dev/null +++ b/telecomm/java/android/telecom/QueryLocationException.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2022 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 android.telecom; +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.os.OutcomeReceiver; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + +/** + * This class represents a set of exceptions that can occur when requesting a + * {@link Connection#queryLocationForEmergency(long, String, Executor, OutcomeReceiver)} + */ +public final class QueryLocationException extends RuntimeException implements Parcelable { + /** @hide */ + public static final String QUERY_LOCATION_ERROR = "QueryLocationErrorKey"; + + /** + * The operation was not completed on time. + */ + public static final int ERROR_REQUEST_TIME_OUT = 1; + /** + * The operation was rejected due to an existing request. + */ + public static final int ERROR_PREVIOUS_REQUEST_EXISTS = 2; + /** + * The operation has failed because it is not permitted. + */ + public static final int ERROR_NOT_PERMITTED = 3; + /** + * The operation has failed due to a location query being requested for a non-emergency + * connection. + */ + public static final int ERROR_NOT_ALLOWED_FOR_NON_EMERGENCY_CONNECTIONS = 4; + /** + * The operation has failed due to the service is not available. + */ + public static final int ERROR_SERVICE_UNAVAILABLE = 5; + /** + * The operation has failed due to an unknown or unspecified error. + */ + public static final int ERROR_UNSPECIFIED = 6; + + private int mCode = ERROR_UNSPECIFIED; + private final String mMessage; + @Override + public int describeContents() { + return 0; + } + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mMessage); + dest.writeInt(mCode); + } + /** + * Responsible for creating QueryLocationException objects for deserialized Parcels. + */ + public static final + @android.annotation.NonNull Parcelable.Creator<QueryLocationException> CREATOR = + new Parcelable.Creator<>() { + @Override + public QueryLocationException createFromParcel(Parcel source) { + return new QueryLocationException(source.readString8(), source.readInt()); + } + @Override + public QueryLocationException[] newArray(int size) { + return new QueryLocationException[size]; + } + }; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ERROR_REQUEST_TIME_OUT, + ERROR_PREVIOUS_REQUEST_EXISTS, + ERROR_NOT_PERMITTED, + ERROR_NOT_ALLOWED_FOR_NON_EMERGENCY_CONNECTIONS, + ERROR_SERVICE_UNAVAILABLE, + ERROR_UNSPECIFIED}) + public @interface QueryLocationErrorCode {} + public QueryLocationException(@Nullable String message) { + super(getMessage(message, ERROR_UNSPECIFIED)); + mMessage = message; + } + public QueryLocationException(@Nullable String message, @QueryLocationErrorCode int code) { + super(getMessage(message, code)); + mCode = code; + mMessage = message; + } + public QueryLocationException( + @Nullable String message, @QueryLocationErrorCode int code, @Nullable Throwable cause) { + super(getMessage(message, code), cause); + mCode = code; + mMessage = message; + } + public @QueryLocationErrorCode int getCode() { + return mCode; + } + private static String getMessage(String message, int code) { + StringBuilder builder; + if (!TextUtils.isEmpty(message)) { + builder = new StringBuilder(message); + builder.append(" (code: "); + builder.append(code); + builder.append(")"); + return builder.toString(); + } else { + return "code: " + code; + } + } +} diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index 65617327e43b..2fc6a22261b6 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -517,6 +517,12 @@ final class RemoteConnectionService { ResultReceiver callback, Session.Info sessionInfo) { // Do nothing } + + @Override + public void queryLocation(String callId, long timeoutMillis, String provider, + ResultReceiver callback, Session.Info sessionInfo) { + // Do nothing + } }; private final ConnectionServiceAdapterServant mServant = diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl index 6838fbd99953..8ac016155aa6 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl @@ -139,4 +139,7 @@ oneway interface IConnectionServiceAdapter { void setConferenceState(String callId, boolean isConference, in Session.Info sessionInfo); void setCallDirection(String callId, int direction, in Session.Info sessionInfo); + + void queryLocation(String callId, long timeoutMillis, String provider, + in ResultReceiver callback, in Session.Info sessionInfo); } |