Loading src/com/android/server/telecom/voip/ParallelTransaction.java 0 → 100644 +90 −0 Original line number Diff line number Diff line /* * 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 com.android.server.telecom.voip; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * A VoipCallTransaction implementation that its sub transactions will be executed in parallel */ public class ParallelTransaction extends VoipCallTransaction { public ParallelTransaction(List<VoipCallTransaction> subTransactions) { super(subTransactions); } @Override public void start() { // post timeout work mHandler.postDelayed(() -> { if (mCompleted.getAndSet(true)) { return; } if (mCompleteListener != null) { mCompleteListener.onTransactionTimeout(mTransactionName); } finish(); }, TIMEOUT_LIMIT); if (mSubTransactions != null && mSubTransactions.size() > 0) { TransactionManager.TransactionCompleteListener subTransactionListener = new TransactionManager.TransactionCompleteListener() { private final AtomicInteger mCount = new AtomicInteger(mSubTransactions.size()); @Override public void onTransactionCompleted(VoipCallTransactionResult result, String transactionName) { if (result.getResult() != VoipCallTransactionResult.RESULT_SUCCEED) { mHandler.post(() -> { VoipCallTransactionResult mainResult = new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, String.format("sub transaction %s failed", transactionName)); mCompleteListener.onTransactionCompleted(mainResult, mTransactionName); finish(); }); } else { if (mCount.decrementAndGet() == 0) { scheduleTransaction(); } } } @Override public void onTransactionTimeout(String transactionName) { mHandler.post(() -> { VoipCallTransactionResult mainResult = new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, String.format("sub transaction %s timed out", transactionName)); mCompleteListener.onTransactionCompleted(mainResult, mTransactionName); finish(); }); } }; for (VoipCallTransaction transaction : mSubTransactions) { transaction.setCompleteListener(subTransactionListener); transaction.start(); } } else { scheduleTransaction(); } } } src/com/android/server/telecom/voip/SerialTransaction.java 0 → 100644 +91 −0 Original line number Diff line number Diff line /* * 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 com.android.server.telecom.voip; import java.util.List; /** * A VoipCallTransaction implementation that its sub transactions will be executed in serial */ public class SerialTransaction extends VoipCallTransaction { public SerialTransaction(List<VoipCallTransaction> subTransactions) { super(subTransactions); } @Override public void start() { // post timeout work mHandler.postDelayed(() -> { if (mCompleted.getAndSet(true)) { return; } if (mCompleteListener != null) { mCompleteListener.onTransactionTimeout(mTransactionName); } finish(); }, TIMEOUT_LIMIT); if (mSubTransactions != null && mSubTransactions.size() > 0) { TransactionManager.TransactionCompleteListener subTransactionListener = new TransactionManager.TransactionCompleteListener() { @Override public void onTransactionCompleted(VoipCallTransactionResult result, String transactionName) { if (result.getResult() != VoipCallTransactionResult.RESULT_SUCCEED) { mHandler.post(() -> { VoipCallTransactionResult mainResult = new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, String.format("sub transaction %s failed", transactionName)); mCompleteListener.onTransactionCompleted(mainResult, mTransactionName); finish(); }); } else { if (mSubTransactions.size() > 0) { VoipCallTransaction transaction = mSubTransactions.remove(0); transaction.setCompleteListener(this); transaction.start(); } else { scheduleTransaction(); } } } @Override public void onTransactionTimeout(String transactionName) { mHandler.post(() -> { VoipCallTransactionResult mainResult = new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, String.format("sub transaction %s timed out", transactionName)); mCompleteListener.onTransactionCompleted(mainResult, mTransactionName); finish(); }); } }; VoipCallTransaction transaction = mSubTransactions.remove(0); transaction.setCompleteListener(subTransactionListener); transaction.start(); } else { scheduleTransaction(); } } } src/com/android/server/telecom/voip/TransactionManager.java 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * 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 com.android.server.telecom.voip; import android.os.OutcomeReceiver; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayDeque; import java.util.Queue; public class TransactionManager { private static final String TAG = "VoipCallTransactionManager"; private static TransactionManager INSTANCE = null; private static final Object sLock = new Object(); private Queue<VoipCallTransaction> mTransactions; private VoipCallTransaction mCurrentTransaction; public interface TransactionCompleteListener { void onTransactionCompleted(VoipCallTransactionResult result, String transactionName); void onTransactionTimeout(String transactionName); } private TransactionManager() { mTransactions = new ArrayDeque<>(); mCurrentTransaction = null; } public static TransactionManager getInstance() { synchronized (sLock) { if (INSTANCE == null) { INSTANCE = new TransactionManager(); } } return INSTANCE; } @VisibleForTesting public static TransactionManager getTestInstance() { return new TransactionManager(); } public void addTransaction(VoipCallTransaction transaction, OutcomeReceiver<VoipCallTransactionResult, Exception> receiver) { synchronized (sLock) { mTransactions.add(transaction); transaction.setCompleteListener(new TransactionCompleteListener() { @Override public void onTransactionCompleted(VoipCallTransactionResult result, String transactionName) { if (result.getResult() == 0 /* TODO: change this to static value in TelecomManager */) { receiver.onResult(result); } else { receiver.onError(new Exception()); } finishTransaction(); } @Override public void onTransactionTimeout(String transactionName) { receiver.onResult(new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, transactionName + " timeout")); finishTransaction(); } }); } startTransactions(); } private void startTransactions() { synchronized (sLock) { if (mTransactions.isEmpty()) { // No transaction waiting for process return; } if (mCurrentTransaction != null) { // Ongoing transaction return; } mCurrentTransaction = mTransactions.poll(); mCurrentTransaction.start(); } } private void finishTransaction() { synchronized (sLock) { mCurrentTransaction = null; } startTransactions(); } @VisibleForTesting public void clear() { synchronized (sLock) { for (VoipCallTransaction transaction : mTransactions) { transaction.finish(); } } } } src/com/android/server/telecom/voip/VoipCallTransaction.java 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * 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 com.android.server.telecom.voip; import android.os.Handler; import android.os.HandlerThread; import com.android.server.telecom.LoggedHandlerExecutor; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; public class VoipCallTransaction { //TODO: add log events protected static final long TIMEOUT_LIMIT = 5000L; protected final AtomicBoolean mCompleted = new AtomicBoolean(false); protected String mTransactionName = this.getClass().getSimpleName(); private HandlerThread mHandlerThread; protected Handler mHandler; protected TransactionManager.TransactionCompleteListener mCompleteListener; protected List<VoipCallTransaction> mSubTransactions; public VoipCallTransaction( List<VoipCallTransaction> subTransactions) { mSubTransactions = subTransactions; mHandlerThread = new HandlerThread(this.toString()); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); } public VoipCallTransaction() { this(null /** mSubTransactions */); } public void start() { // post timeout work mHandler.postDelayed(() -> { if (mCompleted.getAndSet(true)) { return; } if (mCompleteListener != null) { mCompleteListener.onTransactionTimeout(mTransactionName); } finish(); }, TIMEOUT_LIMIT); scheduleTransaction(); } protected void scheduleTransaction() { CompletableFuture<Void> future = CompletableFuture.completedFuture(null); future.thenComposeAsync(this::processTransaction, new LoggedHandlerExecutor(mHandler, mTransactionName + "@" + hashCode() + ".pT", null)) .thenApplyAsync( (Function<VoipCallTransactionResult, Void>) result -> { mCompleted.set(true); if (mCompleteListener != null) { mCompleteListener.onTransactionCompleted(result, mTransactionName); } finish(); return null; }); } public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) { return CompletableFuture.completedFuture( new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED, null)); } public void setCompleteListener(TransactionManager.TransactionCompleteListener listener) { mCompleteListener = listener; } public void finish() { // finish all sub transactions if (mSubTransactions != null && mSubTransactions.size() > 0) { mSubTransactions.forEach(VoipCallTransaction::finish); } mHandlerThread.quit(); } } src/com/android/server/telecom/voip/VoipCallTransactionResult.java 0 → 100644 +62 −0 Original line number Diff line number Diff line /* * 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 com.android.server.telecom.voip; import com.android.server.telecom.Call; import java.util.Objects; public class VoipCallTransactionResult { public static final int RESULT_SUCCEED = 0; public static final int RESULT_FAILED = 1; private int mResult; private String mMessage; private Call mCall; public VoipCallTransactionResult(int result, String message) { mResult = result; mMessage = message; } public VoipCallTransactionResult(int result, Call call, String message) { mResult = result; mCall = call; mMessage = message; } public int getResult() { return mResult; } public String getMessage() { return mMessage; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof VoipCallTransactionResult)) return false; VoipCallTransactionResult that = (VoipCallTransactionResult) o; return mResult == that.mResult && Objects.equals(mMessage, that.mMessage); } @Override public int hashCode() { return Objects.hash(mResult, mMessage); } } Loading
src/com/android/server/telecom/voip/ParallelTransaction.java 0 → 100644 +90 −0 Original line number Diff line number Diff line /* * 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 com.android.server.telecom.voip; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * A VoipCallTransaction implementation that its sub transactions will be executed in parallel */ public class ParallelTransaction extends VoipCallTransaction { public ParallelTransaction(List<VoipCallTransaction> subTransactions) { super(subTransactions); } @Override public void start() { // post timeout work mHandler.postDelayed(() -> { if (mCompleted.getAndSet(true)) { return; } if (mCompleteListener != null) { mCompleteListener.onTransactionTimeout(mTransactionName); } finish(); }, TIMEOUT_LIMIT); if (mSubTransactions != null && mSubTransactions.size() > 0) { TransactionManager.TransactionCompleteListener subTransactionListener = new TransactionManager.TransactionCompleteListener() { private final AtomicInteger mCount = new AtomicInteger(mSubTransactions.size()); @Override public void onTransactionCompleted(VoipCallTransactionResult result, String transactionName) { if (result.getResult() != VoipCallTransactionResult.RESULT_SUCCEED) { mHandler.post(() -> { VoipCallTransactionResult mainResult = new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, String.format("sub transaction %s failed", transactionName)); mCompleteListener.onTransactionCompleted(mainResult, mTransactionName); finish(); }); } else { if (mCount.decrementAndGet() == 0) { scheduleTransaction(); } } } @Override public void onTransactionTimeout(String transactionName) { mHandler.post(() -> { VoipCallTransactionResult mainResult = new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, String.format("sub transaction %s timed out", transactionName)); mCompleteListener.onTransactionCompleted(mainResult, mTransactionName); finish(); }); } }; for (VoipCallTransaction transaction : mSubTransactions) { transaction.setCompleteListener(subTransactionListener); transaction.start(); } } else { scheduleTransaction(); } } }
src/com/android/server/telecom/voip/SerialTransaction.java 0 → 100644 +91 −0 Original line number Diff line number Diff line /* * 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 com.android.server.telecom.voip; import java.util.List; /** * A VoipCallTransaction implementation that its sub transactions will be executed in serial */ public class SerialTransaction extends VoipCallTransaction { public SerialTransaction(List<VoipCallTransaction> subTransactions) { super(subTransactions); } @Override public void start() { // post timeout work mHandler.postDelayed(() -> { if (mCompleted.getAndSet(true)) { return; } if (mCompleteListener != null) { mCompleteListener.onTransactionTimeout(mTransactionName); } finish(); }, TIMEOUT_LIMIT); if (mSubTransactions != null && mSubTransactions.size() > 0) { TransactionManager.TransactionCompleteListener subTransactionListener = new TransactionManager.TransactionCompleteListener() { @Override public void onTransactionCompleted(VoipCallTransactionResult result, String transactionName) { if (result.getResult() != VoipCallTransactionResult.RESULT_SUCCEED) { mHandler.post(() -> { VoipCallTransactionResult mainResult = new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, String.format("sub transaction %s failed", transactionName)); mCompleteListener.onTransactionCompleted(mainResult, mTransactionName); finish(); }); } else { if (mSubTransactions.size() > 0) { VoipCallTransaction transaction = mSubTransactions.remove(0); transaction.setCompleteListener(this); transaction.start(); } else { scheduleTransaction(); } } } @Override public void onTransactionTimeout(String transactionName) { mHandler.post(() -> { VoipCallTransactionResult mainResult = new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, String.format("sub transaction %s timed out", transactionName)); mCompleteListener.onTransactionCompleted(mainResult, mTransactionName); finish(); }); } }; VoipCallTransaction transaction = mSubTransactions.remove(0); transaction.setCompleteListener(subTransactionListener); transaction.start(); } else { scheduleTransaction(); } } }
src/com/android/server/telecom/voip/TransactionManager.java 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * 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 com.android.server.telecom.voip; import android.os.OutcomeReceiver; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayDeque; import java.util.Queue; public class TransactionManager { private static final String TAG = "VoipCallTransactionManager"; private static TransactionManager INSTANCE = null; private static final Object sLock = new Object(); private Queue<VoipCallTransaction> mTransactions; private VoipCallTransaction mCurrentTransaction; public interface TransactionCompleteListener { void onTransactionCompleted(VoipCallTransactionResult result, String transactionName); void onTransactionTimeout(String transactionName); } private TransactionManager() { mTransactions = new ArrayDeque<>(); mCurrentTransaction = null; } public static TransactionManager getInstance() { synchronized (sLock) { if (INSTANCE == null) { INSTANCE = new TransactionManager(); } } return INSTANCE; } @VisibleForTesting public static TransactionManager getTestInstance() { return new TransactionManager(); } public void addTransaction(VoipCallTransaction transaction, OutcomeReceiver<VoipCallTransactionResult, Exception> receiver) { synchronized (sLock) { mTransactions.add(transaction); transaction.setCompleteListener(new TransactionCompleteListener() { @Override public void onTransactionCompleted(VoipCallTransactionResult result, String transactionName) { if (result.getResult() == 0 /* TODO: change this to static value in TelecomManager */) { receiver.onResult(result); } else { receiver.onError(new Exception()); } finishTransaction(); } @Override public void onTransactionTimeout(String transactionName) { receiver.onResult(new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, transactionName + " timeout")); finishTransaction(); } }); } startTransactions(); } private void startTransactions() { synchronized (sLock) { if (mTransactions.isEmpty()) { // No transaction waiting for process return; } if (mCurrentTransaction != null) { // Ongoing transaction return; } mCurrentTransaction = mTransactions.poll(); mCurrentTransaction.start(); } } private void finishTransaction() { synchronized (sLock) { mCurrentTransaction = null; } startTransactions(); } @VisibleForTesting public void clear() { synchronized (sLock) { for (VoipCallTransaction transaction : mTransactions) { transaction.finish(); } } } }
src/com/android/server/telecom/voip/VoipCallTransaction.java 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * 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 com.android.server.telecom.voip; import android.os.Handler; import android.os.HandlerThread; import com.android.server.telecom.LoggedHandlerExecutor; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; public class VoipCallTransaction { //TODO: add log events protected static final long TIMEOUT_LIMIT = 5000L; protected final AtomicBoolean mCompleted = new AtomicBoolean(false); protected String mTransactionName = this.getClass().getSimpleName(); private HandlerThread mHandlerThread; protected Handler mHandler; protected TransactionManager.TransactionCompleteListener mCompleteListener; protected List<VoipCallTransaction> mSubTransactions; public VoipCallTransaction( List<VoipCallTransaction> subTransactions) { mSubTransactions = subTransactions; mHandlerThread = new HandlerThread(this.toString()); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); } public VoipCallTransaction() { this(null /** mSubTransactions */); } public void start() { // post timeout work mHandler.postDelayed(() -> { if (mCompleted.getAndSet(true)) { return; } if (mCompleteListener != null) { mCompleteListener.onTransactionTimeout(mTransactionName); } finish(); }, TIMEOUT_LIMIT); scheduleTransaction(); } protected void scheduleTransaction() { CompletableFuture<Void> future = CompletableFuture.completedFuture(null); future.thenComposeAsync(this::processTransaction, new LoggedHandlerExecutor(mHandler, mTransactionName + "@" + hashCode() + ".pT", null)) .thenApplyAsync( (Function<VoipCallTransactionResult, Void>) result -> { mCompleted.set(true); if (mCompleteListener != null) { mCompleteListener.onTransactionCompleted(result, mTransactionName); } finish(); return null; }); } public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) { return CompletableFuture.completedFuture( new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED, null)); } public void setCompleteListener(TransactionManager.TransactionCompleteListener listener) { mCompleteListener = listener; } public void finish() { // finish all sub transactions if (mSubTransactions != null && mSubTransactions.size() > 0) { mSubTransactions.forEach(VoipCallTransaction::finish); } mHandlerThread.quit(); } }
src/com/android/server/telecom/voip/VoipCallTransactionResult.java 0 → 100644 +62 −0 Original line number Diff line number Diff line /* * 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 com.android.server.telecom.voip; import com.android.server.telecom.Call; import java.util.Objects; public class VoipCallTransactionResult { public static final int RESULT_SUCCEED = 0; public static final int RESULT_FAILED = 1; private int mResult; private String mMessage; private Call mCall; public VoipCallTransactionResult(int result, String message) { mResult = result; mMessage = message; } public VoipCallTransactionResult(int result, Call call, String message) { mResult = result; mCall = call; mMessage = message; } public int getResult() { return mResult; } public String getMessage() { return mMessage; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof VoipCallTransactionResult)) return false; VoipCallTransactionResult that = (VoipCallTransactionResult) o; return mResult == that.mResult && Objects.equals(mMessage, that.mMessage); } @Override public int hashCode() { return Objects.hash(mResult, mMessage); } }