Merge "[MS67.1] Expose registerUsageCallback with template" am: a88cab0f1f am: 3d505ef365

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1956051

Change-Id: Iaadb1473872bcefa2f99d76a79000559cbfd82a0
This commit is contained in:
Junyu Lai
2022-01-25 01:52:23 +00:00
committed by Automerger Merge Worker
5 changed files with 155 additions and 91 deletions

View File

@@ -21,6 +21,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -39,14 +40,11 @@ import android.net.NetworkStack;
import android.net.NetworkStateSnapshot;
import android.net.NetworkTemplate;
import android.net.UnderlyingNetworkInfo;
import android.net.netstats.IUsageCallback;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -57,6 +55,7 @@ import com.android.net.module.util.NetworkIdentityUtils;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* Provides access to network usage history and statistics. Usage data is collected in
@@ -723,26 +722,35 @@ public class NetworkStatsManager {
}
}
/** @hide */
public void registerUsageCallback(NetworkTemplate template, int networkType,
long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
/**
* Registers to receive notifications about data usage on specified networks.
*
* <p>The callbacks will continue to be called as long as the process is alive or
* {@link #unregisterUsageCallback} is called.
*
* @param template Template used to match networks. See {@link NetworkTemplate}.
* @param thresholdBytes Threshold in bytes to be notified on.
* @param executor The executor on which callback will be invoked. The provided {@link Executor}
* must run callback sequentially, otherwise the order of callbacks cannot be
* guaranteed.
* @param callback The {@link UsageCallback} that the system will call when data usage
* has exceeded the specified threshold.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public void registerUsageCallback(@NonNull NetworkTemplate template, long thresholdBytes,
@NonNull @CallbackExecutor Executor executor, @NonNull UsageCallback callback) {
Objects.requireNonNull(template, "NetworkTemplate cannot be null");
Objects.requireNonNull(callback, "UsageCallback cannot be null");
Objects.requireNonNull(executor, "Executor cannot be null");
final Looper looper;
if (handler == null) {
looper = Looper.myLooper();
} else {
looper = handler.getLooper();
}
DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
final DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
template, thresholdBytes);
try {
CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
template.getSubscriberId(), callback);
final UsageCallbackWrapper callbackWrapper =
new UsageCallbackWrapper(executor, callback);
callback.request = mService.registerUsageCallback(
mContext.getOpPackageName(), request, new Messenger(callbackHandler),
new Binder());
mContext.getOpPackageName(), request, callbackWrapper);
if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
if (callback.request == null) {
@@ -795,12 +803,15 @@ public class NetworkStatsManager {
NetworkTemplate template = createTemplate(networkType, subscriberId);
if (DBG) {
Log.d(TAG, "registerUsageCallback called with: {"
+ " networkType=" + networkType
+ " subscriberId=" + subscriberId
+ " thresholdBytes=" + thresholdBytes
+ " }");
+ " networkType=" + networkType
+ " subscriberId=" + subscriberId
+ " thresholdBytes=" + thresholdBytes
+ " }");
}
registerUsageCallback(template, networkType, thresholdBytes, callback, handler);
final Executor executor = handler == null ? r -> r.run() : r -> handler.post(r);
registerUsageCallback(template, thresholdBytes, executor, callback);
}
/**
@@ -825,6 +836,26 @@ public class NetworkStatsManager {
* Base class for usage callbacks. Should be extended by applications wanting notifications.
*/
public static abstract class UsageCallback {
/**
* Called when data usage has reached the given threshold.
*
* Called by {@code NetworkStatsService} when the registered threshold is reached.
* If a caller implements {@link #onThresholdReached(NetworkTemplate)}, the system
* will not call {@link #onThresholdReached(int, String)}.
*
* @param template The {@link NetworkTemplate} that associated with this callback.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public void onThresholdReached(@NonNull NetworkTemplate template) {
// Backward compatibility for those who didn't override this function.
final int networkType = networkTypeForTemplate(template);
if (networkType != ConnectivityManager.TYPE_NONE) {
final String subscriberId = template.getSubscriberIds().isEmpty() ? null
: template.getSubscriberIds().iterator().next();
onThresholdReached(networkType, subscriberId);
}
}
/**
* Called when data usage has reached the given threshold.
@@ -835,6 +866,25 @@ public class NetworkStatsManager {
* @hide used for internal bookkeeping
*/
private DataUsageRequest request;
/**
* Get network type from a template if feasible.
*
* @param template the target {@link NetworkTemplate}.
* @return legacy network type, only supports for the types which is already supported in
* {@link #registerUsageCallback(int, String, long, UsageCallback, Handler)}.
* {@link ConnectivityManager#TYPE_NONE} for other types.
*/
private static int networkTypeForTemplate(@NonNull NetworkTemplate template) {
switch (template.getMatchRule()) {
case NetworkTemplate.MATCH_MOBILE:
return ConnectivityManager.TYPE_MOBILE;
case NetworkTemplate.MATCH_WIFI:
return ConnectivityManager.TYPE_WIFI;
default:
return ConnectivityManager.TYPE_NONE;
}
}
}
/**
@@ -953,43 +1003,32 @@ public class NetworkStatsManager {
}
}
private static class CallbackHandler extends Handler {
private final int mNetworkType;
private final String mSubscriberId;
private UsageCallback mCallback;
private static class UsageCallbackWrapper extends IUsageCallback.Stub {
// Null if unregistered.
private volatile UsageCallback mCallback;
CallbackHandler(Looper looper, int networkType, String subscriberId,
UsageCallback callback) {
super(looper);
mNetworkType = networkType;
mSubscriberId = subscriberId;
private final Executor mExecutor;
UsageCallbackWrapper(@NonNull Executor executor, @NonNull UsageCallback callback) {
mCallback = callback;
mExecutor = executor;
}
@Override
public void handleMessage(Message message) {
DataUsageRequest request =
(DataUsageRequest) getObject(message, DataUsageRequest.PARCELABLE_KEY);
switch (message.what) {
case CALLBACK_LIMIT_REACHED: {
if (mCallback != null) {
mCallback.onThresholdReached(mNetworkType, mSubscriberId);
} else {
Log.e(TAG, "limit reached with released callback for " + request);
}
break;
}
case CALLBACK_RELEASED: {
if (DBG) Log.d(TAG, "callback released for " + request);
mCallback = null;
break;
}
public void onThresholdReached(DataUsageRequest request) {
// Copy it to a local variable in case mCallback changed inside the if condition.
final UsageCallback callback = mCallback;
if (callback != null) {
mExecutor.execute(() -> callback.onThresholdReached(request.template));
} else {
Log.e(TAG, "onThresholdReached with released callback for " + request);
}
}
private static Object getObject(Message msg, String key) {
return msg.getData().getParcelable(key);
@Override
public void onCallbackReleased(DataUsageRequest request) {
if (DBG) Log.d(TAG, "callback released for " + request);
mCallback = null;
}
}

View File

@@ -24,6 +24,7 @@ import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.UnderlyingNetworkInfo;
import android.net.netstats.IUsageCallback;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.IBinder;
@@ -71,7 +72,7 @@ interface INetworkStatsService {
/** Registers a callback on data usage. */
DataUsageRequest registerUsageCallback(String callingPackage,
in DataUsageRequest request, in Messenger messenger, in IBinder binder);
in DataUsageRequest request, in IUsageCallback callback);
/** Unregisters a callback on data usage. */
void unregisterUsageRequest(in DataUsageRequest request);

View File

@@ -0,0 +1,29 @@
/*
* 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.net.netstats;
import android.net.DataUsageRequest;
/**
* Interface for NetworkStatsService to notify events to the callers of registerUsageCallback.
*
* @hide
*/
oneway interface IUsageCallback {
void onThresholdReached(in DataUsageRequest request);
void onCallbackReleased(in DataUsageRequest request);
}

View File

@@ -26,13 +26,12 @@ import android.net.NetworkStatsAccess;
import android.net.NetworkStatsCollection;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.Bundle;
import android.net.netstats.IUsageCallback;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -75,10 +74,10 @@ class NetworkStatsObservers {
*
* @return the normalized request wrapped within {@link RequestInfo}.
*/
public DataUsageRequest register(DataUsageRequest inputRequest, Messenger messenger,
IBinder binder, int callingUid, @NetworkStatsAccess.Level int accessLevel) {
public DataUsageRequest register(DataUsageRequest inputRequest, IUsageCallback callback,
int callingUid, @NetworkStatsAccess.Level int accessLevel) {
DataUsageRequest request = buildRequest(inputRequest);
RequestInfo requestInfo = buildRequestInfo(request, messenger, binder, callingUid,
RequestInfo requestInfo = buildRequestInfo(request, callback, callingUid,
accessLevel);
if (LOGV) Log.v(TAG, "Registering observer for " + request);
@@ -206,11 +205,10 @@ class NetworkStatsObservers {
request.template, thresholdInBytes);
}
private RequestInfo buildRequestInfo(DataUsageRequest request,
Messenger messenger, IBinder binder, int callingUid,
@NetworkStatsAccess.Level int accessLevel) {
private RequestInfo buildRequestInfo(DataUsageRequest request, IUsageCallback callback,
int callingUid, @NetworkStatsAccess.Level int accessLevel) {
if (accessLevel <= NetworkStatsAccess.Level.USER) {
return new UserUsageRequestInfo(this, request, messenger, binder, callingUid,
return new UserUsageRequestInfo(this, request, callback, callingUid,
accessLevel);
} else {
// Safety check in case a new access level is added and we forgot to update this
@@ -218,7 +216,7 @@ class NetworkStatsObservers {
throw new IllegalArgumentException(
"accessLevel " + accessLevel + " is less than DEVICESUMMARY.");
}
return new NetworkUsageRequestInfo(this, request, messenger, binder, callingUid,
return new NetworkUsageRequestInfo(this, request, callback, callingUid,
accessLevel);
}
}
@@ -230,25 +228,23 @@ class NetworkStatsObservers {
private abstract static class RequestInfo implements IBinder.DeathRecipient {
private final NetworkStatsObservers mStatsObserver;
protected final DataUsageRequest mRequest;
private final Messenger mMessenger;
private final IBinder mBinder;
private final IUsageCallback mCallback;
protected final int mCallingUid;
protected final @NetworkStatsAccess.Level int mAccessLevel;
protected NetworkStatsRecorder mRecorder;
protected NetworkStatsCollection mCollection;
RequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
Messenger messenger, IBinder binder, int callingUid,
IUsageCallback callback, int callingUid,
@NetworkStatsAccess.Level int accessLevel) {
mStatsObserver = statsObserver;
mRequest = request;
mMessenger = messenger;
mBinder = binder;
mCallback = callback;
mCallingUid = callingUid;
mAccessLevel = accessLevel;
try {
mBinder.linkToDeath(this, 0);
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
binderDied();
}
@@ -257,7 +253,7 @@ class NetworkStatsObservers {
@Override
public void binderDied() {
if (LOGV) {
Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mBinder + ")");
Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mCallback + ")");
}
mStatsObserver.unregister(mRequest, Process.SYSTEM_UID);
callCallback(NetworkStatsManager.CALLBACK_RELEASED);
@@ -270,9 +266,7 @@ class NetworkStatsObservers {
}
private void unlinkDeathRecipient() {
if (mBinder != null) {
mBinder.unlinkToDeath(this, 0);
}
mCallback.asBinder().unlinkToDeath(this, 0);
}
/**
@@ -294,17 +288,19 @@ class NetworkStatsObservers {
}
private void callCallback(int callbackType) {
Bundle bundle = new Bundle();
bundle.putParcelable(DataUsageRequest.PARCELABLE_KEY, mRequest);
Message msg = Message.obtain();
msg.what = callbackType;
msg.setData(bundle);
try {
if (LOGV) {
Log.v(TAG, "sending notification " + callbackTypeToName(callbackType)
+ " for " + mRequest);
}
mMessenger.send(msg);
switch (callbackType) {
case NetworkStatsManager.CALLBACK_LIMIT_REACHED:
mCallback.onThresholdReached(mRequest);
break;
case NetworkStatsManager.CALLBACK_RELEASED:
mCallback.onCallbackReleased(mRequest);
break;
}
} catch (RemoteException e) {
// May occur naturally in the race of binder death.
Log.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
@@ -334,9 +330,9 @@ class NetworkStatsObservers {
private static class NetworkUsageRequestInfo extends RequestInfo {
NetworkUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
Messenger messenger, IBinder binder, int callingUid,
IUsageCallback callback, int callingUid,
@NetworkStatsAccess.Level int accessLevel) {
super(statsObserver, request, messenger, binder, callingUid, accessLevel);
super(statsObserver, request, callback, callingUid, accessLevel);
}
@Override
@@ -376,9 +372,9 @@ class NetworkStatsObservers {
private static class UserUsageRequestInfo extends RequestInfo {
UserUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
Messenger messenger, IBinder binder, int callingUid,
IUsageCallback callback, int callingUid,
@NetworkStatsAccess.Level int accessLevel) {
super(statsObserver, request, messenger, binder, callingUid, accessLevel);
super(statsObserver, request, callback, callingUid, accessLevel);
}
@Override

View File

@@ -99,6 +99,7 @@ import android.net.TetheringManager;
import android.net.TrafficStats;
import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
import android.net.netstats.IUsageCallback;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
@@ -110,7 +111,6 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -1148,21 +1148,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
@Override
public DataUsageRequest registerUsageCallback(String callingPackage,
DataUsageRequest request, Messenger messenger, IBinder binder) {
public DataUsageRequest registerUsageCallback(@NonNull String callingPackage,
@NonNull DataUsageRequest request, @NonNull IUsageCallback callback) {
Objects.requireNonNull(callingPackage, "calling package is null");
Objects.requireNonNull(request, "DataUsageRequest is null");
Objects.requireNonNull(request.template, "NetworkTemplate is null");
Objects.requireNonNull(messenger, "messenger is null");
Objects.requireNonNull(binder, "binder is null");
Objects.requireNonNull(callback, "callback is null");
int callingUid = Binder.getCallingUid();
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(callingPackage);
DataUsageRequest normalizedRequest;
final long token = Binder.clearCallingIdentity();
try {
normalizedRequest = mStatsObservers.register(request, messenger, binder,
callingUid, accessLevel);
normalizedRequest = mStatsObservers.register(
request, callback, callingUid, accessLevel);
} finally {
Binder.restoreCallingIdentity(token);
}