Merge changes from topics "ms25-appops", "ms28-service-fix"
* changes: [MS25.1] Remove AppOpsManager.OP_GET_USAGE_STATS dependency [MS28.1] Fix several hidden API dependencies
This commit is contained in:
@@ -192,8 +192,8 @@ public final class NetworkStatsAccess {
|
||||
AppOpsManager appOps = (AppOpsManager) context.getSystemService(
|
||||
Context.APP_OPS_SERVICE);
|
||||
|
||||
final int mode = appOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS,
|
||||
callingUid, callingPackage);
|
||||
final int mode = appOps.noteOp(AppOpsManager.OPSTR_GET_USAGE_STATS,
|
||||
callingUid, callingPackage, null /* attributionTag */, null /* message */);
|
||||
if (mode == AppOpsManager.MODE_DEFAULT) {
|
||||
// The default behavior here is to check if PackageManager has given the app
|
||||
// permission.
|
||||
|
||||
@@ -35,8 +35,8 @@ import android.os.SystemClock;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.ProcFileReader;
|
||||
import com.android.net.module.util.CollectionUtils;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
@@ -434,7 +434,7 @@ public class NetworkStatsFactory {
|
||||
entry.txBytes = reader.nextLong();
|
||||
entry.txPackets = reader.nextLong();
|
||||
|
||||
if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
|
||||
if ((limitIfaces == null || CollectionUtils.contains(limitIfaces, entry.iface))
|
||||
&& (limitUid == UID_ALL || limitUid == entry.uid)
|
||||
&& (limitTag == TAG_ALL || limitTag == entry.tag)) {
|
||||
stats.insertEntry(entry);
|
||||
|
||||
@@ -38,7 +38,7 @@ import android.os.Messenger;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -83,7 +83,7 @@ class NetworkStatsObservers {
|
||||
RequestInfo requestInfo = buildRequestInfo(request, messenger, binder, callingUid,
|
||||
accessLevel);
|
||||
|
||||
if (LOGV) Slog.v(TAG, "Registering observer for " + request);
|
||||
if (LOGV) Log.v(TAG, "Registering observer for " + request);
|
||||
getHandler().sendMessage(mHandler.obtainMessage(MSG_REGISTER, requestInfo));
|
||||
return request;
|
||||
}
|
||||
@@ -116,7 +116,7 @@ class NetworkStatsObservers {
|
||||
if (mHandler == null) {
|
||||
synchronized (this) {
|
||||
if (mHandler == null) {
|
||||
if (LOGV) Slog.v(TAG, "Creating handler");
|
||||
if (LOGV) Log.v(TAG, "Creating handler");
|
||||
mHandler = new Handler(getHandlerLooperLocked(), mHandlerCallback);
|
||||
}
|
||||
}
|
||||
@@ -172,15 +172,15 @@ class NetworkStatsObservers {
|
||||
RequestInfo requestInfo;
|
||||
requestInfo = mDataUsageRequests.get(request.requestId);
|
||||
if (requestInfo == null) {
|
||||
if (LOGV) Slog.v(TAG, "Trying to unregister unknown request " + request);
|
||||
if (LOGV) Log.v(TAG, "Trying to unregister unknown request " + request);
|
||||
return;
|
||||
}
|
||||
if (Process.SYSTEM_UID != callingUid && requestInfo.mCallingUid != callingUid) {
|
||||
Slog.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
|
||||
Log.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (LOGV) Slog.v(TAG, "Unregistering " + request);
|
||||
if (LOGV) Log.v(TAG, "Unregistering " + request);
|
||||
mDataUsageRequests.remove(request.requestId);
|
||||
requestInfo.unlinkDeathRecipient();
|
||||
requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED);
|
||||
@@ -201,7 +201,7 @@ class NetworkStatsObservers {
|
||||
// Cap the minimum threshold to a safe default to avoid too many callbacks
|
||||
long thresholdInBytes = Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes);
|
||||
if (thresholdInBytes < request.thresholdInBytes) {
|
||||
Slog.w(TAG, "Threshold was too low for " + request
|
||||
Log.w(TAG, "Threshold was too low for " + request
|
||||
+ ". Overriding to a safer default of " + thresholdInBytes + " bytes");
|
||||
}
|
||||
return new DataUsageRequest(mNextDataUsageRequestId.incrementAndGet(),
|
||||
@@ -255,8 +255,9 @@ class NetworkStatsObservers {
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
if (LOGV) Slog.v(TAG, "RequestInfo binderDied("
|
||||
+ mRequest + ", " + mBinder + ")");
|
||||
if (LOGV) {
|
||||
Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mBinder + ")");
|
||||
}
|
||||
mStatsObserver.unregister(mRequest, Process.SYSTEM_UID);
|
||||
callCallback(NetworkStatsManager.CALLBACK_RELEASED);
|
||||
}
|
||||
@@ -299,13 +300,13 @@ class NetworkStatsObservers {
|
||||
msg.setData(bundle);
|
||||
try {
|
||||
if (LOGV) {
|
||||
Slog.v(TAG, "sending notification " + callbackTypeToName(callbackType)
|
||||
Log.v(TAG, "sending notification " + callbackTypeToName(callbackType)
|
||||
+ " for " + mRequest);
|
||||
}
|
||||
mMessenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
// May occur naturally in the race of binder death.
|
||||
Slog.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
|
||||
Log.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,7 +342,7 @@ class NetworkStatsObservers {
|
||||
protected boolean checkStats() {
|
||||
long bytesSoFar = getTotalBytesForNetwork(mRequest.template);
|
||||
if (LOGV) {
|
||||
Slog.v(TAG, bytesSoFar + " bytes so far since notification for "
|
||||
Log.v(TAG, bytesSoFar + " bytes so far since notification for "
|
||||
+ mRequest.template);
|
||||
}
|
||||
if (bytesSoFar > mRequest.thresholdInBytes) {
|
||||
@@ -416,7 +417,7 @@ class NetworkStatsObservers {
|
||||
return history.getTotalBytes();
|
||||
} catch (SecurityException e) {
|
||||
if (LOGV) {
|
||||
Slog.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
|
||||
Log.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
|
||||
+ uid);
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -32,13 +32,12 @@ import android.net.TrafficStats;
|
||||
import android.os.Binder;
|
||||
import android.os.DropBoxManager;
|
||||
import android.service.NetworkStatsRecorderProto;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Slog;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.internal.util.FileRotator;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
|
||||
import com.google.android.collect.Sets;
|
||||
|
||||
@@ -132,7 +131,7 @@ public class NetworkStatsRecorder {
|
||||
}
|
||||
|
||||
public void setPersistThreshold(long thresholdBytes) {
|
||||
if (LOGV) Slog.v(TAG, "setPersistThreshold() with " + thresholdBytes);
|
||||
if (LOGV) Log.v(TAG, "setPersistThreshold() with " + thresholdBytes);
|
||||
mPersistThresholdBytes = MathUtils.constrain(
|
||||
thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES);
|
||||
}
|
||||
@@ -185,7 +184,7 @@ public class NetworkStatsRecorder {
|
||||
}
|
||||
|
||||
private NetworkStatsCollection loadLocked(long start, long end) {
|
||||
if (LOGD) Slog.d(TAG, "loadLocked() reading from disk for " + mCookie);
|
||||
if (LOGD) Log.d(TAG, "loadLocked() reading from disk for " + mCookie);
|
||||
final NetworkStatsCollection res = new NetworkStatsCollection(mBucketDuration);
|
||||
try {
|
||||
mRotator.readMatching(res, start, end);
|
||||
@@ -272,7 +271,7 @@ public class NetworkStatsRecorder {
|
||||
mLastSnapshot = snapshot;
|
||||
|
||||
if (LOGV && unknownIfaces.size() > 0) {
|
||||
Slog.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
|
||||
Log.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +295,7 @@ public class NetworkStatsRecorder {
|
||||
public void forcePersistLocked(long currentTimeMillis) {
|
||||
Objects.requireNonNull(mRotator, "missing FileRotator");
|
||||
if (mPending.isDirty()) {
|
||||
if (LOGD) Slog.d(TAG, "forcePersistLocked() writing for " + mCookie);
|
||||
if (LOGD) Log.d(TAG, "forcePersistLocked() writing for " + mCookie);
|
||||
try {
|
||||
mRotator.rewriteActive(mPendingRewriter, currentTimeMillis);
|
||||
mRotator.maybeRotate(currentTimeMillis);
|
||||
|
||||
@@ -141,19 +141,18 @@ import android.text.format.DateUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.EventLog;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.util.FileRotator;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.net.module.util.BinderUtils;
|
||||
import com.android.net.module.util.CollectionUtils;
|
||||
import com.android.net.module.util.NetworkStatsUtils;
|
||||
import com.android.net.module.util.PermissionUtils;
|
||||
import com.android.server.EventLogTags;
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
@@ -163,6 +162,7 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.time.Clock;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -646,7 +646,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
try {
|
||||
mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
|
||||
} catch (IllegalStateException e) {
|
||||
Slog.w(TAG, "problem registering for global alert: " + e);
|
||||
Log.w(TAG, "problem registering for global alert: " + e);
|
||||
} catch (RemoteException e) {
|
||||
// ignored; service lives in system_server
|
||||
}
|
||||
@@ -764,7 +764,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
return stats;
|
||||
} catch (NullPointerException e) {
|
||||
// TODO: Track down and fix the cause of this crash and remove this catch block.
|
||||
Slog.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
|
||||
Log.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -821,7 +821,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
SubscriptionPlan plan = null;
|
||||
if ((flags & NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN) != 0
|
||||
&& mSettings.getAugmentEnabled()) {
|
||||
if (LOGD) Slog.d(TAG, "Resolving plan for " + template);
|
||||
if (LOGD) Log.d(TAG, "Resolving plan for " + template);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
|
||||
@@ -829,7 +829,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
if (LOGD) Slog.d(TAG, "Resolved to plan " + plan);
|
||||
if (LOGD) Log.d(TAG, "Resolved to plan " + plan);
|
||||
}
|
||||
return plan;
|
||||
}
|
||||
@@ -932,7 +932,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
@Override
|
||||
public String[] getMobileIfaces() {
|
||||
// TODO (b/192758557): Remove debug log.
|
||||
if (ArrayUtils.contains(mMobileIfaces, null)) {
|
||||
if (CollectionUtils.contains(mMobileIfaces, null)) {
|
||||
throw new NullPointerException(
|
||||
"null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
|
||||
}
|
||||
@@ -1010,9 +1010,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
|
||||
private void advisePersistThreshold(long thresholdBytes) {
|
||||
// clamp threshold into safe range
|
||||
mPersistThreshold = MathUtils.constrain(thresholdBytes, 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
|
||||
mPersistThreshold = NetworkStatsUtils.constrain(thresholdBytes,
|
||||
128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
|
||||
if (LOGV) {
|
||||
Slog.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
|
||||
Log.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
|
||||
+ mPersistThreshold);
|
||||
}
|
||||
|
||||
@@ -1275,7 +1276,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
private void handleNotifyNetworkStatusLocked(@NonNull Network[] defaultNetworks,
|
||||
@NonNull NetworkStateSnapshot[] snapshots) {
|
||||
if (!mSystemReady) return;
|
||||
if (LOGV) Slog.v(TAG, "handleNotifyNetworkStatusLocked()");
|
||||
if (LOGV) Log.v(TAG, "handleNotifyNetworkStatusLocked()");
|
||||
|
||||
// take one last stats snapshot before updating iface mapping. this
|
||||
// isn't perfect, since the kernel may already be counting traffic from
|
||||
@@ -1299,7 +1300,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
final int displayTransport =
|
||||
getDisplayTransport(snapshot.getNetworkCapabilities().getTransportTypes());
|
||||
final boolean isMobile = (NetworkCapabilities.TRANSPORT_CELLULAR == displayTransport);
|
||||
final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, snapshot.getNetwork());
|
||||
final boolean isDefault = CollectionUtils.contains(
|
||||
mDefaultNetworks, snapshot.getNetwork());
|
||||
final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
|
||||
: getSubTypeForStateSnapshot(snapshot);
|
||||
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
|
||||
@@ -1382,7 +1384,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
|
||||
mMobileIfaces = mobileIfaces.toArray(new String[0]);
|
||||
// TODO (b/192758557): Remove debug log.
|
||||
if (ArrayUtils.contains(mMobileIfaces, null)) {
|
||||
if (CollectionUtils.contains(mMobileIfaces, null)) {
|
||||
throw new NullPointerException(
|
||||
"null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
|
||||
}
|
||||
@@ -1397,7 +1399,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
if (spec instanceof TelephonyNetworkSpecifier) {
|
||||
return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
|
||||
} else {
|
||||
Slog.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
|
||||
Log.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
|
||||
return INVALID_SUBSCRIPTION_ID;
|
||||
}
|
||||
}
|
||||
@@ -1481,7 +1483,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
try {
|
||||
recordSnapshotLocked(currentTime);
|
||||
} catch (IllegalStateException e) {
|
||||
Slog.w(TAG, "problem reading network stats: " + e);
|
||||
Log.w(TAG, "problem reading network stats: " + e);
|
||||
} catch (RemoteException e) {
|
||||
// ignored; service lives in system_server
|
||||
}
|
||||
@@ -1506,7 +1508,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
@GuardedBy("mStatsLock")
|
||||
private void performPollLocked(int flags) {
|
||||
if (!mSystemReady) return;
|
||||
if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
|
||||
if (LOGV) Log.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
|
||||
Trace.traceBegin(TRACE_TAG_NETWORK, "performPollLocked");
|
||||
|
||||
final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
|
||||
@@ -1628,7 +1630,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
*/
|
||||
@GuardedBy("mStatsLock")
|
||||
private void removeUidsLocked(int... uids) {
|
||||
if (LOGV) Slog.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
|
||||
if (LOGV) Log.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
|
||||
|
||||
// Perform one last poll before removing
|
||||
performPollLocked(FLAG_PERSIST_ALL);
|
||||
@@ -1647,19 +1649,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
*/
|
||||
@GuardedBy("mStatsLock")
|
||||
private void removeUserLocked(int userId) {
|
||||
if (LOGV) Slog.v(TAG, "removeUserLocked() for userId=" + userId);
|
||||
if (LOGV) Log.v(TAG, "removeUserLocked() for userId=" + userId);
|
||||
|
||||
// Build list of UIDs that we should clean up
|
||||
int[] uids = new int[0];
|
||||
final ArrayList<Integer> uids = new ArrayList<>();
|
||||
final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
|
||||
PackageManager.MATCH_ANY_USER
|
||||
| PackageManager.MATCH_DISABLED_COMPONENTS);
|
||||
for (ApplicationInfo app : apps) {
|
||||
final int uid = UserHandle.getUid(userId, app.uid);
|
||||
uids = ArrayUtils.appendInt(uids, uid);
|
||||
uids.add(uid);
|
||||
}
|
||||
|
||||
removeUidsLocked(uids);
|
||||
removeUidsLocked(CollectionUtils.toIntArray(uids));
|
||||
}
|
||||
|
||||
private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
|
||||
@@ -1702,7 +1704,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
public void setStatsProviderWarningAndLimitAsync(
|
||||
@NonNull String iface, long warning, long limit) {
|
||||
if (LOGV) {
|
||||
Slog.v(TAG, "setStatsProviderWarningAndLimitAsync("
|
||||
Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
|
||||
+ iface + "," + warning + "," + limit + ")");
|
||||
}
|
||||
invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
|
||||
@@ -1712,7 +1714,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
|
||||
@Override
|
||||
protected void dump(FileDescriptor fd, PrintWriter rawWriter, String[] args) {
|
||||
if (!DumpUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
|
||||
if (!PermissionUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
|
||||
|
||||
long duration = DateUtils.DAY_IN_MILLIS;
|
||||
final HashSet<String> argSet = new HashSet<String>();
|
||||
@@ -1773,15 +1775,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
|
||||
pw.println("Configs:");
|
||||
pw.increaseIndent();
|
||||
pw.printPair(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
|
||||
pw.print(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
|
||||
pw.println();
|
||||
pw.decreaseIndent();
|
||||
|
||||
pw.println("Active interfaces:");
|
||||
pw.increaseIndent();
|
||||
for (int i = 0; i < mActiveIfaces.size(); i++) {
|
||||
pw.printPair("iface", mActiveIfaces.keyAt(i));
|
||||
pw.printPair("ident", mActiveIfaces.valueAt(i));
|
||||
pw.print("iface", mActiveIfaces.keyAt(i));
|
||||
pw.print("ident", mActiveIfaces.valueAt(i));
|
||||
pw.println();
|
||||
}
|
||||
pw.decreaseIndent();
|
||||
@@ -1789,8 +1791,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
pw.println("Active UID interfaces:");
|
||||
pw.increaseIndent();
|
||||
for (int i = 0; i < mActiveUidIfaces.size(); i++) {
|
||||
pw.printPair("iface", mActiveUidIfaces.keyAt(i));
|
||||
pw.printPair("ident", mActiveUidIfaces.valueAt(i));
|
||||
pw.print("iface", mActiveUidIfaces.keyAt(i));
|
||||
pw.print("ident", mActiveUidIfaces.valueAt(i));
|
||||
pw.println();
|
||||
}
|
||||
pw.decreaseIndent();
|
||||
|
||||
Reference in New Issue
Block a user