summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSarah Chin <sarahchin@google.com>2021-09-08 16:12:28 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2021-09-08 16:12:28 +0000
commite34b1b87357e291320e6d307c1762dbab652d23f (patch)
treee42f2081d37a965b354030e2084ade1640a55a6d
parent42d6f9b7b27e3f1901b96850b6f0c5fa3a94e7cb (diff)
parent1c8976b843367145afcacd1890aa553745c27cac (diff)
downloadbase-android-s-beta-5.tar.gz
Merge "Expose requestModemActivityInfo"android-s-beta-5android-s-beta-5
-rw-r--r--core/api/current.txt5
-rw-r--r--core/api/system-current.txt9
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/os/OutcomeReceiver.java42
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java40
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java2
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java33
-rw-r--r--telephony/java/android/telephony/ModemActivityInfo.java8
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java144
9 files changed, 259 insertions, 25 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 0d8e3a775147..b2f633726494 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29936,6 +29936,11 @@ package android.os {
ctor public OperationCanceledException(String);
}
+ public interface OutcomeReceiver<R, E extends java.lang.Throwable> {
+ method public default void onError(@NonNull E);
+ method public void onResult(@NonNull R);
+ }
+
public final class Parcel {
method public void appendFrom(android.os.Parcel, int, int);
method @Nullable public android.os.IBinder[] createBinderArray();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 84c602faffe9..3e423e6908fd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10356,6 +10356,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestModemActivityInfo(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.ModemActivityInfo,android.telephony.TelephonyManager.ModemActivityInfoException>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
@@ -10528,6 +10529,14 @@ package android.telephony {
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception {
+ method public int getErrorCode();
+ field public static final int ERROR_INVALID_INFO_RECEIVED = 2; // 0x2
+ field public static final int ERROR_MODEM_RESPONSE_ERROR = 3; // 0x3
+ field public static final int ERROR_PHONE_NOT_AVAILABLE = 1; // 0x1
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ }
+
public final class ThermalMitigationRequest implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.telephony.DataThrottlingRequest getDataThrottlingRequest();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 95de4e34ed7c..d905bbeba6b5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1660,6 +1660,7 @@ package android.telephony {
method public long getTimestampMillis();
method public long getTransmitDurationMillisAtPowerLevel(int);
method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+ method public boolean isEmpty();
method public boolean isValid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
diff --git a/core/java/android/os/OutcomeReceiver.java b/core/java/android/os/OutcomeReceiver.java
new file mode 100644
index 000000000000..01b276411446
--- /dev/null
+++ b/core/java/android/os/OutcomeReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.NonNull;
+
+/**
+ * Callback interface intended for use when an asynchronous operation may result in a failure.
+ *
+ * This interface may be used in cases where an asynchronous API may complete either with a value
+ * or with a {@link Throwable} that indicates an error.
+ * @param <R> The type of the result that's being sent.
+ * @param <E> The type of the {@link Throwable} that contains more information about the error.
+ */
+public interface OutcomeReceiver<R, E extends Throwable> {
+ /**
+ * Called when the asynchronous operation succeeds and delivers a result value.
+ * @param result The value delivered by the asynchronous operation.
+ */
+ void onResult(@NonNull R result);
+
+ /**
+ * Called when the asynchronous operation fails. The mode of failure is indicated by the
+ * {@link Throwable} passed as an argument to this method.
+ * @param error A subclass of {@link Throwable} with more details about the error that occurred.
+ */
+ default void onError(@NonNull E error) {}
+}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 39f79ca2f13b..ef47b1ebc7ec 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.net.wifi.WifiManager;
import android.os.BatteryStats;
import android.os.Bundle;
+import android.os.OutcomeReceiver;
import android.os.Parcelable;
import android.os.Process;
import android.os.ServiceManager;
@@ -40,6 +41,7 @@ import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.function.pooled.PooledLambda;
+import java.util.concurrent.ExecutionException;
import libcore.util.EmptyArray;
import java.util.concurrent.CompletableFuture;
@@ -405,7 +407,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
// We will request data from external processes asynchronously, and wait on a timeout.
SynchronousResultReceiver wifiReceiver = null;
SynchronousResultReceiver bluetoothReceiver = null;
- SynchronousResultReceiver modemReceiver = null;
+ CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null);
boolean railUpdated = false;
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
@@ -460,8 +462,22 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
if (mTelephony != null) {
- modemReceiver = new SynchronousResultReceiver("telephony");
- mTelephony.requestModemActivityInfo(modemReceiver);
+ CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>();
+ mTelephony.requestModemActivityInfo(Runnable::run,
+ new OutcomeReceiver<ModemActivityInfo,
+ TelephonyManager.ModemActivityInfoException>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ temp.complete(result);
+ }
+
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Slog.w(TAG, "error reading modem stats:" + e);
+ temp.complete(null);
+ }
+ });
+ modemFuture = temp;
}
if (!railUpdated) {
synchronized (mStats) {
@@ -472,7 +488,17 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
final BluetoothActivityEnergyInfo bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
- final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+ ModemActivityInfo modemInfo = null;
+ try {
+ modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | InterruptedException e) {
+ Slog.w(TAG, "timeout or interrupt reading modem stats: " + e);
+ } catch (ExecutionException e) {
+ Slog.w(TAG, "exception reading modem stats: " + e.getCause());
+ }
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
synchronized (mStats) {
mStats.addHistoryEventLocked(
@@ -519,11 +545,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
if (modemInfo != null) {
- if (modemInfo.isValid()) {
- mStats.updateMobileRadioState(modemInfo);
- } else {
- Slog.w(TAG, "modem info is invalid: " + modemInfo);
- }
+ mStats.updateMobileRadioState(modemInfo);
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 580ceca3b197..34ba3e078860 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1208,7 +1208,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
public void noteModemControllerActivity(ModemActivityInfo info) {
enforceCallingPermission();
- if (info == null || !info.isValid()) {
+ if (info == null) {
Slog.e(TAG, "invalid modem data given: " + info);
return;
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index a24a6532290f..71b3e61a9844 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -102,6 +102,7 @@ import android.os.Environment;
import android.os.IStoraged;
import android.os.IThermalEventListener;
import android.os.IThermalService;
+import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -172,6 +173,7 @@ import com.android.server.stats.pull.netstats.SubInfo;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
+import java.util.concurrent.ExecutionException;
import libcore.io.IoUtils;
import org.json.JSONArray;
@@ -1731,9 +1733,34 @@ public class StatsPullAtomService extends SystemService {
int pullModemActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) {
long token = Binder.clearCallingIdentity();
try {
- SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
- mTelephony.requestModemActivityInfo(modemReceiver);
- final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+ CompletableFuture<ModemActivityInfo> modemFuture = new CompletableFuture<>();
+ mTelephony.requestModemActivityInfo(Runnable::run,
+ new OutcomeReceiver<ModemActivityInfo,
+ TelephonyManager.ModemActivityInfoException>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ modemFuture.complete(result);
+ }
+
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Slog.w(TAG, "error reading modem stats:" + e);
+ modemFuture.complete(null);
+ }
+ });
+
+ ModemActivityInfo modemInfo;
+ try {
+ modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | InterruptedException e) {
+ Slog.w(TAG, "timeout or interrupt reading modem stats: " + e);
+ return StatsManager.PULL_SKIP;
+ } catch (ExecutionException e) {
+ Slog.w(TAG, "exception reading modem stats: " + e.getCause());
+ return StatsManager.PULL_SKIP;
+ }
+
if (modemInfo == null) {
return StatsManager.PULL_SKIP;
}
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index 881d85c73b5d..0bf8ce620eb7 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -131,7 +131,7 @@ public final class ModemActivityInfo implements Parcelable {
+ " mTimestamp=" + mTimestamp
+ " mSleepTimeMs=" + mSleepTimeMs
+ " mIdleTimeMs=" + mIdleTimeMs
- + " mTxTimeMs[]=" + mTxTimeMs
+ + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs)
+ " mRxTimeMs=" + mRxTimeMs
+ "}";
}
@@ -320,8 +320,6 @@ public final class ModemActivityInfo implements Parcelable {
*
* @return {@code true} if this {@link ModemActivityInfo} record is valid,
* {@code false} otherwise.
- * TODO: remove usages of this outside Telephony by always returning a valid (or null) result
- * from telephony.
* @hide
*/
@TestApi
@@ -332,7 +330,9 @@ public final class ModemActivityInfo implements Parcelable {
&& (getReceiveTimeMillis() >= 0) && !isEmpty());
}
- private boolean isEmpty() {
+ /** @hide */
+ @TestApi
+ public boolean isEmpty() {
boolean isTxPowerEmpty = mTxTimeMs == null || mTxTimeMs.length == 0
|| Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4189784bf5e0..b8a173ed5aee 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -57,7 +57,9 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -176,6 +178,9 @@ public class TelephonyManager {
*/
public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
+ /** @hide */
+ public static final String EXCEPTION_RESULT_KEY = "exception";
+
/**
* The process name of the Phone app as well as many other apps that use this process name, such
* as settings and vendor components.
@@ -10855,26 +10860,149 @@ public class TelephonyManager {
return null;
}
+ /**
+ * Exception that may be supplied to the callback provided in {@link #requestModemActivityInfo}.
+ * @hide
+ */
+ @SystemApi
+ public static class ModemActivityInfoException extends Exception {
+ /** Indicates that an unknown error occurred */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Indicates that the modem or phone processes are not available (such as when the device
+ * is in airplane mode).
+ */
+ public static final int ERROR_PHONE_NOT_AVAILABLE = 1;
+
+ /**
+ * Indicates that the modem supplied an invalid instance of {@link ModemActivityInfo}
+ */
+ public static final int ERROR_INVALID_INFO_RECEIVED = 2;
+
+ /**
+ * Indicates that the modem encountered an internal failure when processing the request
+ * for activity info.
+ */
+ public static final int ERROR_MODEM_RESPONSE_ERROR = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ERROR_"},
+ value = {
+ ERROR_UNKNOWN,
+ ERROR_PHONE_NOT_AVAILABLE,
+ ERROR_INVALID_INFO_RECEIVED,
+ ERROR_MODEM_RESPONSE_ERROR,
+ })
+ public @interface ModemActivityInfoError {}
+
+ private final int mErrorCode;
+
+ /** @hide */
+ public ModemActivityInfoException(@ModemActivityInfoError int errorCode) {
+ mErrorCode = errorCode;
+ }
+
+ public @ModemActivityInfoError int getErrorCode() {
+ return mErrorCode;
+ }
+
+ @Override
+ public String toString() {
+ switch (mErrorCode) {
+ case ERROR_UNKNOWN: return "ERROR_UNKNOWN";
+ case ERROR_PHONE_NOT_AVAILABLE: return "ERROR_PHONE_NOT_AVAILABLE";
+ case ERROR_INVALID_INFO_RECEIVED: return "ERROR_INVALID_INFO_RECEIVED";
+ case ERROR_MODEM_RESPONSE_ERROR: return "ERROR_MODEM_RESPONSE_ERROR";
+ default: return "UNDEFINED";
+ }
+ }
+ }
/**
- * Requests the modem activity info. The recipient will place the result
- * in `result`.
- * @param result The object on which the recipient will send the resulting
- * {@link android.telephony.ModemActivityInfo} object with key of
- * {@link #MODEM_ACTIVITY_RESULT_KEY}.
+ * Requests the current modem activity info.
+ *
+ * The provided instance of {@link ModemActivityInfo} represents the cumulative activity since
+ * the last restart of the phone process.
+ *
+ * @param callback A callback object to which the result will be delivered. If there was an
+ * error processing the request, {@link OutcomeReceiver#onError} will be called
+ * with more details about the error.
* @hide
*/
- public void requestModemActivityInfo(@NonNull ResultReceiver result) {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ // Pass no handler into the receiver, since we're going to be trampolining the call to the
+ // listener onto the provided executor.
+ ResultReceiver wrapperResultReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle data) {
+ if (data == null) {
+ Log.w(TAG, "requestModemActivityInfo: received null bundle");
+ sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+ return;
+ }
+ data.setDefusable(true);
+ if (data.containsKey(EXCEPTION_RESULT_KEY)) {
+ int receivedErrorCode = data.getInt(EXCEPTION_RESULT_KEY);
+ sendErrorToListener(receivedErrorCode);
+ return;
+ }
+
+ if (!data.containsKey(MODEM_ACTIVITY_RESULT_KEY)) {
+ Log.w(TAG, "requestModemActivityInfo: Bundle did not contain expected key");
+ sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+ return;
+ }
+ Parcelable receivedResult = data.getParcelable(MODEM_ACTIVITY_RESULT_KEY);
+ if (!(receivedResult instanceof ModemActivityInfo)) {
+ Log.w(TAG, "requestModemActivityInfo: Bundle contained something that wasn't "
+ + "a ModemActivityInfo.");
+ sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+ return;
+ }
+ ModemActivityInfo modemActivityInfo = (ModemActivityInfo) receivedResult;
+ if (!modemActivityInfo.isValid()) {
+ Log.w(TAG, "requestModemActivityInfo: Received an invalid ModemActivityInfo");
+ sendErrorToListener(ModemActivityInfoException.ERROR_INVALID_INFO_RECEIVED);
+ return;
+ }
+ Log.d(TAG, "requestModemActivityInfo: Sending result to app: " + modemActivityInfo);
+ sendResultToListener(modemActivityInfo);
+ }
+
+ private void sendResultToListener(ModemActivityInfo info) {
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() ->
+ callback.onResult(info)));
+ }
+
+ private void sendErrorToListener(int code) {
+ ModemActivityInfoException e = new ModemActivityInfoException(code);
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() ->
+ callback.onError(e)));
+ }
+ };
+
try {
ITelephony service = getITelephony();
if (service != null) {
- service.requestModemActivityInfo(result);
+ service.requestModemActivityInfo(wrapperResultReceiver);
return;
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e);
}
- result.send(0, null);
+ executor.execute(() -> callback.onError(
+ new ModemActivityInfoException(
+ ModemActivityInfoException.ERROR_PHONE_NOT_AVAILABLE)));
}
/**