Merge changes from topic "pacproxy-service" am: 16f50075b1 am: 688ae36dcb
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1553959 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Ife210de3adbdaff6c88caf744d805571b365b294
This commit is contained in:
@@ -58,6 +58,16 @@ package android.net {
|
|||||||
method @Nullable public byte[] getWatchlistConfigHash();
|
method @Nullable public byte[] getWatchlistConfigHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class PacProxyManager {
|
||||||
|
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void addPacProxyInstalledListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.PacProxyManager.PacProxyInstalledListener);
|
||||||
|
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void removePacProxyInstalledListener(@NonNull android.net.PacProxyManager.PacProxyInstalledListener);
|
||||||
|
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setCurrentProxyScriptUrl(@Nullable android.net.ProxyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface PacProxyManager.PacProxyInstalledListener {
|
||||||
|
method public void onPacProxyInstalled(@Nullable android.net.Network, @NonNull android.net.ProxyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
public final class Proxy {
|
public final class Proxy {
|
||||||
method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
|
method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,11 +117,13 @@ import android.net.EthernetManager;
|
|||||||
import android.net.IEthernetManager;
|
import android.net.IEthernetManager;
|
||||||
import android.net.IIpSecService;
|
import android.net.IIpSecService;
|
||||||
import android.net.INetworkPolicyManager;
|
import android.net.INetworkPolicyManager;
|
||||||
|
import android.net.IPacProxyManager;
|
||||||
import android.net.IVpnManager;
|
import android.net.IVpnManager;
|
||||||
import android.net.IpSecManager;
|
import android.net.IpSecManager;
|
||||||
import android.net.NetworkPolicyManager;
|
import android.net.NetworkPolicyManager;
|
||||||
import android.net.NetworkScoreManager;
|
import android.net.NetworkScoreManager;
|
||||||
import android.net.NetworkWatchlistManager;
|
import android.net.NetworkWatchlistManager;
|
||||||
|
import android.net.PacProxyManager;
|
||||||
import android.net.TetheringManager;
|
import android.net.TetheringManager;
|
||||||
import android.net.VpnManager;
|
import android.net.VpnManager;
|
||||||
import android.net.lowpan.ILowpanManager;
|
import android.net.lowpan.ILowpanManager;
|
||||||
@@ -346,6 +348,15 @@ public final class SystemServiceRegistry {
|
|||||||
// (which extends it).
|
// (which extends it).
|
||||||
SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
|
SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
|
||||||
|
|
||||||
|
registerService(Context.PAC_PROXY_SERVICE, PacProxyManager.class,
|
||||||
|
new CachedServiceFetcher<PacProxyManager>() {
|
||||||
|
@Override
|
||||||
|
public PacProxyManager createService(ContextImpl ctx) throws ServiceNotFoundException {
|
||||||
|
IBinder b = ServiceManager.getServiceOrThrow(Context.PAC_PROXY_SERVICE);
|
||||||
|
IPacProxyManager service = IPacProxyManager.Stub.asInterface(b);
|
||||||
|
return new PacProxyManager(ctx.getOuterContext(), service);
|
||||||
|
}});
|
||||||
|
|
||||||
registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
|
registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
|
||||||
@Override
|
@Override
|
||||||
public IBinder createService() throws ServiceNotFoundException {
|
public IBinder createService() throws ServiceNotFoundException {
|
||||||
|
|||||||
@@ -3451,6 +3451,7 @@ public abstract class Context {
|
|||||||
VIBRATOR_SERVICE,
|
VIBRATOR_SERVICE,
|
||||||
//@hide: STATUS_BAR_SERVICE,
|
//@hide: STATUS_BAR_SERVICE,
|
||||||
CONNECTIVITY_SERVICE,
|
CONNECTIVITY_SERVICE,
|
||||||
|
PAC_PROXY_SERVICE,
|
||||||
VCN_MANAGEMENT_SERVICE,
|
VCN_MANAGEMENT_SERVICE,
|
||||||
//@hide: IP_MEMORY_STORE_SERVICE,
|
//@hide: IP_MEMORY_STORE_SERVICE,
|
||||||
IPSEC_SERVICE,
|
IPSEC_SERVICE,
|
||||||
@@ -4016,6 +4017,17 @@ public abstract class Context {
|
|||||||
*/
|
*/
|
||||||
public static final String CONNECTIVITY_SERVICE = "connectivity";
|
public static final String CONNECTIVITY_SERVICE = "connectivity";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use with {@link #getSystemService(String)} to retrieve a {@link
|
||||||
|
* android.net.PacProxyManager} for handling management of
|
||||||
|
* pac proxy information.
|
||||||
|
*
|
||||||
|
* @see #getSystemService(String)
|
||||||
|
* @see android.net.PacProxyManager
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String PAC_PROXY_SERVICE = "pac_proxy";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use with {@link #getSystemService(String)} to retrieve a {@link android.net.vcn.VcnManager}
|
* Use with {@link #getSystemService(String)} to retrieve a {@link android.net.vcn.VcnManager}
|
||||||
* for managing Virtual Carrier Networks
|
* for managing Virtual Carrier Networks
|
||||||
|
|||||||
25
core/java/android/net/IPacProxyInstalledListener.aidl
Normal file
25
core/java/android/net/IPacProxyInstalledListener.aidl
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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;
|
||||||
|
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.ProxyInfo;
|
||||||
|
|
||||||
|
/** {@hide} */
|
||||||
|
oneway interface IPacProxyInstalledListener {
|
||||||
|
void onPacProxyInstalled(in Network network, in ProxyInfo proxy);
|
||||||
|
}
|
||||||
28
core/java/android/net/IPacProxyManager.aidl
Normal file
28
core/java/android/net/IPacProxyManager.aidl
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021, 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 perNmissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net;
|
||||||
|
|
||||||
|
import android.net.IPacProxyInstalledListener;
|
||||||
|
import android.net.ProxyInfo;
|
||||||
|
|
||||||
|
/** {@hide} */
|
||||||
|
interface IPacProxyManager
|
||||||
|
{
|
||||||
|
void addListener(IPacProxyInstalledListener listener);
|
||||||
|
void removeListener(IPacProxyInstalledListener listener);
|
||||||
|
void setCurrentProxyScriptUrl(in ProxyInfo proxyInfo);
|
||||||
|
}
|
||||||
150
core/java/android/net/PacProxyManager.java
Normal file
150
core/java/android/net/PacProxyManager.java
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.annotation.RequiresPermission;
|
||||||
|
import android.annotation.SystemApi;
|
||||||
|
import android.annotation.SystemService;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
||||||
|
@SystemService(Context.PAC_PROXY_SERVICE)
|
||||||
|
public class PacProxyManager {
|
||||||
|
private final Context mContext;
|
||||||
|
private final IPacProxyManager mService;
|
||||||
|
@GuardedBy("mListenerMap")
|
||||||
|
private final HashMap<PacProxyInstalledListener, PacProxyInstalledListenerProxy>
|
||||||
|
mListenerMap = new HashMap<>();
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public PacProxyManager(Context context, IPacProxyManager service) {
|
||||||
|
Objects.requireNonNull(service, "missing IPacProxyManager");
|
||||||
|
mContext = context;
|
||||||
|
mService = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a listener to start monitoring events reported by PacProxyService.
|
||||||
|
*/
|
||||||
|
@RequiresPermission(anyOf = {
|
||||||
|
android.Manifest.permission.NETWORK_STACK,
|
||||||
|
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
|
||||||
|
android.Manifest.permission.NETWORK_SETTINGS})
|
||||||
|
public void addPacProxyInstalledListener(@NonNull Executor executor,
|
||||||
|
@NonNull PacProxyInstalledListener listener) {
|
||||||
|
try {
|
||||||
|
synchronized (mListenerMap) {
|
||||||
|
final PacProxyInstalledListenerProxy listenerProxy =
|
||||||
|
new PacProxyInstalledListenerProxy(executor, listener);
|
||||||
|
|
||||||
|
if (null != mListenerMap.putIfAbsent(listener, listenerProxy)) {
|
||||||
|
throw new IllegalStateException("Listener is already added.");
|
||||||
|
}
|
||||||
|
mService.addListener(listenerProxy);
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the listener to stop monitoring the event of PacProxyInstalledListener.
|
||||||
|
*/
|
||||||
|
@RequiresPermission(anyOf = {
|
||||||
|
android.Manifest.permission.NETWORK_STACK,
|
||||||
|
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
|
||||||
|
android.Manifest.permission.NETWORK_SETTINGS})
|
||||||
|
public void removePacProxyInstalledListener(@NonNull PacProxyInstalledListener listener) {
|
||||||
|
try {
|
||||||
|
synchronized (mListenerMap) {
|
||||||
|
final PacProxyInstalledListenerProxy listenerProxy = mListenerMap.remove(listener);
|
||||||
|
if (listenerProxy == null) return;
|
||||||
|
mService.removeListener(listenerProxy);
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the PAC Proxy Installer with current Proxy information.
|
||||||
|
*/
|
||||||
|
@RequiresPermission(anyOf = {
|
||||||
|
android.Manifest.permission.NETWORK_STACK,
|
||||||
|
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
|
||||||
|
android.Manifest.permission.NETWORK_SETTINGS})
|
||||||
|
public void setCurrentProxyScriptUrl(@Nullable ProxyInfo proxy) {
|
||||||
|
try {
|
||||||
|
mService.setCurrentProxyScriptUrl(proxy);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback interface for monitoring changes of PAC proxy information.
|
||||||
|
*/
|
||||||
|
public interface PacProxyInstalledListener {
|
||||||
|
/**
|
||||||
|
* Notify that the PAC proxy has been installed. Note that this method will be called with
|
||||||
|
* a ProxyInfo with an empty PAC URL when the PAC proxy is removed.
|
||||||
|
*
|
||||||
|
* This method supports different PAC proxies per-network but not all devices might support
|
||||||
|
* per-network proxies. In that case it will be applied globally.
|
||||||
|
*
|
||||||
|
* @param network the network for which this proxy installed.
|
||||||
|
* @param proxy the installed proxy.
|
||||||
|
*/
|
||||||
|
void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PacProxyInstalledListener proxy for PacProxyInstalledListener object.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class PacProxyInstalledListenerProxy extends IPacProxyInstalledListener.Stub {
|
||||||
|
private final Executor mExecutor;
|
||||||
|
private final PacProxyInstalledListener mListener;
|
||||||
|
|
||||||
|
PacProxyInstalledListenerProxy(Executor executor, PacProxyInstalledListener listener) {
|
||||||
|
mExecutor = executor;
|
||||||
|
mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacProxyInstalled(Network network, ProxyInfo proxy) {
|
||||||
|
Binder.withCleanCallingIdentity(() -> {
|
||||||
|
mExecutor.execute(() -> {
|
||||||
|
mListener.onPacProxyInstalled(network, proxy);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,7 +51,7 @@ public class PacProxySelector extends ProxySelector {
|
|||||||
ServiceManager.getService(PROXY_SERVICE));
|
ServiceManager.getService(PROXY_SERVICE));
|
||||||
if (mProxyService == null) {
|
if (mProxyService == null) {
|
||||||
// Added because of b10267814 where mako is restarting.
|
// Added because of b10267814 where mako is restarting.
|
||||||
Log.e(TAG, "PacProxyInstaller: no proxy service");
|
Log.e(TAG, "PacProxyService: no proxy service");
|
||||||
}
|
}
|
||||||
mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
|
mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public class ProxyInfo implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only used in PacProxyInstaller after Local Proxy is bound.
|
* Only used in PacProxyService after Local Proxy is bound.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
|
public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ public class ProxyServer extends Thread {
|
|||||||
try {
|
try {
|
||||||
mCallback.setProxyPort(port);
|
mCallback.setProxyPort(port);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
|
Log.w(TAG, "Proxy failed to report port to PacProxyService", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mPort = port;
|
mPort = port;
|
||||||
@@ -371,7 +371,7 @@ public class ProxyServer extends Thread {
|
|||||||
try {
|
try {
|
||||||
callback.setProxyPort(mPort);
|
callback.setProxyPort(mPort);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
|
Log.w(TAG, "Proxy failed to report port to PacProxyService", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mCallback = callback;
|
mCallback = callback;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class ProxyService extends Service {
|
|||||||
|
|
||||||
private static ProxyServer server = null;
|
private static ProxyServer server = null;
|
||||||
|
|
||||||
/** Keep these values up-to-date with PacProxyInstaller.java */
|
/** Keep these values up-to-date with PacProxyService.java */
|
||||||
public static final String KEY_PROXY = "keyProxy";
|
public static final String KEY_PROXY = "keyProxy";
|
||||||
public static final String HOST = "localhost";
|
public static final String HOST = "localhost";
|
||||||
public static final String EXCL_LIST = "";
|
public static final String EXCL_LIST = "";
|
||||||
|
|||||||
@@ -4423,7 +4423,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EVENT_PROXY_HAS_CHANGED: {
|
case EVENT_PROXY_HAS_CHANGED: {
|
||||||
handleApplyDefaultProxy((ProxyInfo)msg.obj);
|
final Pair<Network, ProxyInfo> arg = (Pair<Network, ProxyInfo>) msg.obj;
|
||||||
|
handleApplyDefaultProxy(arg.second);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EVENT_REGISTER_NETWORK_PROVIDER: {
|
case EVENT_REGISTER_NETWORK_PROVIDER: {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.server.connectivity;
|
package com.android.server.connectivity;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.annotation.WorkerThread;
|
import android.annotation.WorkerThread;
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
@@ -26,12 +28,16 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
|
import android.net.IPacProxyInstalledListener;
|
||||||
|
import android.net.IPacProxyManager;
|
||||||
import android.net.ProxyInfo;
|
import android.net.ProxyInfo;
|
||||||
import android.net.TrafficStats;
|
import android.net.TrafficStats;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerExecutor;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
@@ -44,6 +50,7 @@ import com.android.internal.util.TrafficStatsConstants;
|
|||||||
import com.android.net.IProxyCallback;
|
import com.android.net.IProxyCallback;
|
||||||
import com.android.net.IProxyPortListener;
|
import com.android.net.IProxyPortListener;
|
||||||
import com.android.net.IProxyService;
|
import com.android.net.IProxyService;
|
||||||
|
import com.android.net.module.util.PermissionUtils;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -53,7 +60,7 @@ import java.net.URLConnection;
|
|||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class PacProxyInstaller {
|
public class PacProxyService extends IPacProxyManager.Stub {
|
||||||
private static final String PAC_PACKAGE = "com.android.pacprocessor";
|
private static final String PAC_PACKAGE = "com.android.pacprocessor";
|
||||||
private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
|
private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
|
||||||
private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
|
private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
|
||||||
@@ -61,7 +68,7 @@ public class PacProxyInstaller {
|
|||||||
private static final String PROXY_PACKAGE = "com.android.proxyhandler";
|
private static final String PROXY_PACKAGE = "com.android.proxyhandler";
|
||||||
private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
|
private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
|
||||||
|
|
||||||
private static final String TAG = "PacProxyInstaller";
|
private static final String TAG = "PacProxyService";
|
||||||
|
|
||||||
private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
|
private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
|
||||||
|
|
||||||
@@ -71,10 +78,6 @@ public class PacProxyInstaller {
|
|||||||
private static final int DELAY_LONG = 4;
|
private static final int DELAY_LONG = 4;
|
||||||
private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
|
private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
|
||||||
|
|
||||||
// Return values for #setCurrentProxyScriptUrl
|
|
||||||
public static final boolean DONT_SEND_BROADCAST = false;
|
|
||||||
public static final boolean DO_SEND_BROADCAST = true;
|
|
||||||
|
|
||||||
private String mCurrentPac;
|
private String mCurrentPac;
|
||||||
@GuardedBy("mProxyLock")
|
@GuardedBy("mProxyLock")
|
||||||
private volatile Uri mPacUrl = Uri.EMPTY;
|
private volatile Uri mPacUrl = Uri.EMPTY;
|
||||||
@@ -93,14 +96,21 @@ public class PacProxyInstaller {
|
|||||||
private volatile boolean mHasSentBroadcast;
|
private volatile boolean mHasSentBroadcast;
|
||||||
private volatile boolean mHasDownloaded;
|
private volatile boolean mHasDownloaded;
|
||||||
|
|
||||||
private Handler mConnectivityHandler;
|
private final RemoteCallbackList<IPacProxyInstalledListener>
|
||||||
private final int mProxyMessage;
|
mCallbacks = new RemoteCallbackList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for locking when setting mProxyService and all references to mCurrentPac.
|
* Used for locking when setting mProxyService and all references to mCurrentPac.
|
||||||
*/
|
*/
|
||||||
private final Object mProxyLock = new Object();
|
private final Object mProxyLock = new Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
|
||||||
|
* last URL and port, and the broadcast message being sent with the correct arguments.
|
||||||
|
* TODO : this should probably protect all instances of these variables
|
||||||
|
*/
|
||||||
|
private final Object mBroadcastStateLock = new Object();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runnable to download PAC script.
|
* Runnable to download PAC script.
|
||||||
* The behavior relies on the assumption it always runs on mNetThread to guarantee that the
|
* The behavior relies on the assumption it always runs on mNetThread to guarantee that the
|
||||||
@@ -146,10 +156,10 @@ public class PacProxyInstaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
|
public PacProxyService(@NonNull Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mLastPort = -1;
|
mLastPort = -1;
|
||||||
final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
|
final HandlerThread netThread = new HandlerThread("android.pacproxyservice",
|
||||||
android.os.Process.THREAD_PRIORITY_DEFAULT);
|
android.os.Process.THREAD_PRIORITY_DEFAULT);
|
||||||
netThread.start();
|
netThread.start();
|
||||||
mNetThreadHandler = new Handler(netThread.getLooper());
|
mNetThreadHandler = new Handler(netThread.getLooper());
|
||||||
@@ -158,8 +168,6 @@ public class PacProxyInstaller {
|
|||||||
context, 0, new Intent(ACTION_PAC_REFRESH), PendingIntent.FLAG_IMMUTABLE);
|
context, 0, new Intent(ACTION_PAC_REFRESH), PendingIntent.FLAG_IMMUTABLE);
|
||||||
context.registerReceiver(new PacRefreshIntentReceiver(),
|
context.registerReceiver(new PacRefreshIntentReceiver(),
|
||||||
new IntentFilter(ACTION_PAC_REFRESH));
|
new IntentFilter(ACTION_PAC_REFRESH));
|
||||||
mConnectivityHandler = handler;
|
|
||||||
mProxyMessage = proxyMessage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AlarmManager getAlarmManager() {
|
private AlarmManager getAlarmManager() {
|
||||||
@@ -169,28 +177,42 @@ public class PacProxyInstaller {
|
|||||||
return mAlarmManager;
|
return mAlarmManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addListener(IPacProxyInstalledListener listener) {
|
||||||
|
PermissionUtils.enforceNetworkStackPermissionOr(mContext,
|
||||||
|
android.Manifest.permission.NETWORK_SETTINGS);
|
||||||
|
mCallbacks.register(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeListener(IPacProxyInstalledListener listener) {
|
||||||
|
PermissionUtils.enforceNetworkStackPermissionOr(mContext,
|
||||||
|
android.Manifest.permission.NETWORK_SETTINGS);
|
||||||
|
mCallbacks.unregister(listener);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the PAC Proxy Installer with current Proxy information. This is called by
|
* Updates the PAC Proxy Installer with current Proxy information. This is called by
|
||||||
* the ProxyTracker directly before a broadcast takes place to allow
|
* the ProxyTracker through PacProxyManager before a broadcast takes place to allow
|
||||||
* the PacProxyInstaller to indicate that the broadcast should not be sent and the
|
* the PacProxyService to indicate that the broadcast should not be sent and the
|
||||||
* PacProxyInstaller will trigger a new broadcast when it is ready.
|
* PacProxyService will trigger a new broadcast when it is ready.
|
||||||
*
|
*
|
||||||
* @param proxy Proxy information that is about to be broadcast.
|
* @param proxy Proxy information that is about to be broadcast.
|
||||||
* @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
|
|
||||||
*/
|
*/
|
||||||
public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
|
@Override
|
||||||
if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
|
public void setCurrentProxyScriptUrl(@Nullable ProxyInfo proxy) {
|
||||||
if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
|
PermissionUtils.enforceNetworkStackPermissionOr(mContext,
|
||||||
// Allow to send broadcast, nothing to do.
|
android.Manifest.permission.NETWORK_SETTINGS);
|
||||||
return DO_SEND_BROADCAST;
|
|
||||||
}
|
synchronized (mBroadcastStateLock) {
|
||||||
|
if (proxy != null && !Uri.EMPTY.equals(proxy.getPacFileUrl())) {
|
||||||
|
if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
|
||||||
mPacUrl = proxy.getPacFileUrl();
|
mPacUrl = proxy.getPacFileUrl();
|
||||||
mCurrentDelay = DELAY_1;
|
mCurrentDelay = DELAY_1;
|
||||||
mHasSentBroadcast = false;
|
mHasSentBroadcast = false;
|
||||||
mHasDownloaded = false;
|
mHasDownloaded = false;
|
||||||
getAlarmManager().cancel(mPacRefreshIntent);
|
getAlarmManager().cancel(mPacRefreshIntent);
|
||||||
bind();
|
bind();
|
||||||
return DONT_SEND_BROADCAST;
|
|
||||||
} else {
|
} else {
|
||||||
getAlarmManager().cancel(mPacRefreshIntent);
|
getAlarmManager().cancel(mPacRefreshIntent);
|
||||||
synchronized (mProxyLock) {
|
synchronized (mProxyLock) {
|
||||||
@@ -200,7 +222,7 @@ public class PacProxyInstaller {
|
|||||||
unbind();
|
unbind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return DO_SEND_BROADCAST;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,6 +297,7 @@ public class PacProxyInstaller {
|
|||||||
getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
|
getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mProxyLock")
|
||||||
private void setCurrentProxyScript(String script) {
|
private void setCurrentProxyScript(String script) {
|
||||||
if (mProxyService == null) {
|
if (mProxyService == null) {
|
||||||
Log.e(TAG, "setCurrentProxyScript: no proxy service");
|
Log.e(TAG, "setCurrentProxyScript: no proxy service");
|
||||||
@@ -347,6 +370,9 @@ public class PacProxyInstaller {
|
|||||||
public void setProxyPort(int port) {
|
public void setProxyPort(int port) {
|
||||||
if (mLastPort != -1) {
|
if (mLastPort != -1) {
|
||||||
// Always need to send if port changed
|
// Always need to send if port changed
|
||||||
|
// TODO: Here lacks synchronization because this write cannot
|
||||||
|
// guarantee that it's visible from sendProxyIfNeeded() when
|
||||||
|
// it's called by a Runnable which is post by mNetThread.
|
||||||
mHasSentBroadcast = false;
|
mHasSentBroadcast = false;
|
||||||
}
|
}
|
||||||
mLastPort = port;
|
mLastPort = port;
|
||||||
@@ -365,8 +391,9 @@ public class PacProxyInstaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mContext.bindService(intent, mProxyConnection,
|
mContext.bindService(intent,
|
||||||
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
|
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
|
||||||
|
new HandlerExecutor(mNetThreadHandler), mProxyConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unbind() {
|
private void unbind() {
|
||||||
@@ -383,10 +410,21 @@ public class PacProxyInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendPacBroadcast(ProxyInfo proxy) {
|
private void sendPacBroadcast(ProxyInfo proxy) {
|
||||||
mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
|
final int length = mCallbacks.beginBroadcast();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
final IPacProxyInstalledListener listener = mCallbacks.getBroadcastItem(i);
|
||||||
|
if (listener != null) {
|
||||||
|
try {
|
||||||
|
listener.onPacProxyInstalled(null /* network */, proxy);
|
||||||
|
} catch (RemoteException ignored) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mCallbacks.finishBroadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void sendProxyIfNeeded() {
|
// This method must be called on mNetThreadHandler.
|
||||||
|
private void sendProxyIfNeeded() {
|
||||||
|
synchronized (mBroadcastStateLock) {
|
||||||
if (!mHasDownloaded || (mLastPort == -1)) {
|
if (!mHasDownloaded || (mLastPort == -1)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -395,4 +433,5 @@ public class PacProxyInstaller {
|
|||||||
mHasSentBroadcast = true;
|
mHasSentBroadcast = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -27,15 +27,19 @@ import android.annotation.Nullable;
|
|||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.PacProxyManager;
|
||||||
import android.net.Proxy;
|
import android.net.Proxy;
|
||||||
import android.net.ProxyInfo;
|
import android.net.ProxyInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerExecutor;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.net.module.util.ProxyUtils;
|
import com.android.net.module.util.ProxyUtils;
|
||||||
@@ -67,7 +71,7 @@ public class ProxyTracker {
|
|||||||
// is not set. Individual networks have their own settings that override this. This member
|
// is not set. Individual networks have their own settings that override this. This member
|
||||||
// is set through setDefaultProxy, which is called when the default network changes proxies
|
// is set through setDefaultProxy, which is called when the default network changes proxies
|
||||||
// in its LinkProperties, or when ConnectivityService switches to a new default network, or
|
// in its LinkProperties, or when ConnectivityService switches to a new default network, or
|
||||||
// when PacProxyInstaller resolves the proxy.
|
// when PacProxyService resolves the proxy.
|
||||||
@Nullable
|
@Nullable
|
||||||
@GuardedBy("mProxyLock")
|
@GuardedBy("mProxyLock")
|
||||||
private volatile ProxyInfo mDefaultProxy = null;
|
private volatile ProxyInfo mDefaultProxy = null;
|
||||||
@@ -77,16 +81,31 @@ public class ProxyTracker {
|
|||||||
|
|
||||||
private final Handler mConnectivityServiceHandler;
|
private final Handler mConnectivityServiceHandler;
|
||||||
|
|
||||||
// The object responsible for Proxy Auto Configuration (PAC).
|
private final PacProxyManager mPacProxyManager;
|
||||||
@NonNull
|
|
||||||
private final PacProxyInstaller mPacProxyInstaller;
|
private class PacProxyInstalledListener implements PacProxyManager.PacProxyInstalledListener {
|
||||||
|
private final int mEvent;
|
||||||
|
|
||||||
|
PacProxyInstalledListener(int event) {
|
||||||
|
mEvent = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy) {
|
||||||
|
mConnectivityServiceHandler
|
||||||
|
.sendMessage(mConnectivityServiceHandler
|
||||||
|
.obtainMessage(mEvent, new Pair<>(network, proxy)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ProxyTracker(@NonNull final Context context,
|
public ProxyTracker(@NonNull final Context context,
|
||||||
@NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
|
@NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mConnectivityServiceHandler = connectivityServiceInternalHandler;
|
mConnectivityServiceHandler = connectivityServiceInternalHandler;
|
||||||
mPacProxyInstaller = new PacProxyInstaller(
|
mPacProxyManager = context.getSystemService(PacProxyManager.class);
|
||||||
context, connectivityServiceInternalHandler, pacChangedEvent);
|
|
||||||
|
PacProxyInstalledListener listener = new PacProxyInstalledListener(pacChangedEvent);
|
||||||
|
mPacProxyManager.addPacProxyInstalledListener(
|
||||||
|
new HandlerExecutor(mConnectivityServiceHandler), listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
|
// Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
|
||||||
@@ -182,7 +201,7 @@ public class ProxyTracker {
|
|||||||
|
|
||||||
if (!TextUtils.isEmpty(pacFileUrl)) {
|
if (!TextUtils.isEmpty(pacFileUrl)) {
|
||||||
mConnectivityServiceHandler.post(
|
mConnectivityServiceHandler.post(
|
||||||
() -> mPacProxyInstaller.setCurrentProxyScriptUrl(proxyProperties));
|
() -> mPacProxyManager.setCurrentProxyScriptUrl(proxyProperties));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,9 +245,9 @@ public class ProxyTracker {
|
|||||||
final ProxyInfo defaultProxy = getDefaultProxy();
|
final ProxyInfo defaultProxy = getDefaultProxy();
|
||||||
final ProxyInfo proxyInfo = null != defaultProxy ?
|
final ProxyInfo proxyInfo = null != defaultProxy ?
|
||||||
defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
|
defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
|
||||||
|
mPacProxyManager.setCurrentProxyScriptUrl(proxyInfo);
|
||||||
|
|
||||||
if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
|
if (!shouldSendBroadcast(proxyInfo)) {
|
||||||
== PacProxyInstaller.DONT_SEND_BROADCAST) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
|
if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
|
||||||
@@ -244,6 +263,10 @@ public class ProxyTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldSendBroadcast(ProxyInfo proxy) {
|
||||||
|
return Uri.EMPTY.equals(proxy.getPacFileUrl()) || proxy.getPort() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the global proxy in memory. Also writes the values to the global settings of the device.
|
* Sets the global proxy in memory. Also writes the values to the global settings of the device.
|
||||||
*
|
*
|
||||||
@@ -308,10 +331,10 @@ public class ProxyTracker {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This call could be coming from the PacProxyInstaller, containing the port of the
|
// This call could be coming from the PacProxyService, containing the port of the
|
||||||
// local proxy. If this new proxy matches the global proxy then copy this proxy to the
|
// local proxy. If this new proxy matches the global proxy then copy this proxy to the
|
||||||
// global (to get the correct local port), and send a broadcast.
|
// global (to get the correct local port), and send a broadcast.
|
||||||
// TODO: Switch PacProxyInstaller to have its own message to send back rather than
|
// TODO: Switch PacProxyService to have its own message to send back rather than
|
||||||
// reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
|
// reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
|
||||||
if ((mGlobalProxy != null) && (proxyInfo != null)
|
if ((mGlobalProxy != null) && (proxyInfo != null)
|
||||||
&& (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
|
&& (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ import com.android.server.camera.CameraServiceProxy;
|
|||||||
import com.android.server.clipboard.ClipboardService;
|
import com.android.server.clipboard.ClipboardService;
|
||||||
import com.android.server.compat.PlatformCompat;
|
import com.android.server.compat.PlatformCompat;
|
||||||
import com.android.server.compat.PlatformCompatNative;
|
import com.android.server.compat.PlatformCompatNative;
|
||||||
|
import com.android.server.connectivity.PacProxyService;
|
||||||
import com.android.server.contentcapture.ContentCaptureManagerInternal;
|
import com.android.server.contentcapture.ContentCaptureManagerInternal;
|
||||||
import com.android.server.coverage.CoverageService;
|
import com.android.server.coverage.CoverageService;
|
||||||
import com.android.server.devicepolicy.DevicePolicyManagerService;
|
import com.android.server.devicepolicy.DevicePolicyManagerService;
|
||||||
@@ -1115,6 +1116,7 @@ public final class SystemServer {
|
|||||||
ConsumerIrService consumerIr = null;
|
ConsumerIrService consumerIr = null;
|
||||||
MmsServiceBroker mmsService = null;
|
MmsServiceBroker mmsService = null;
|
||||||
HardwarePropertiesManagerService hardwarePropertiesService = null;
|
HardwarePropertiesManagerService hardwarePropertiesService = null;
|
||||||
|
PacProxyService pacProxyService = null;
|
||||||
|
|
||||||
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
|
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
|
||||||
"config.disable_systemtextclassifier", false);
|
"config.disable_systemtextclassifier", false);
|
||||||
@@ -1623,6 +1625,15 @@ public final class SystemServer {
|
|||||||
t.traceEnd();
|
t.traceEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.traceBegin("StartPacProxyService");
|
||||||
|
try {
|
||||||
|
pacProxyService = new PacProxyService(context);
|
||||||
|
ServiceManager.addService(Context.PAC_PROXY_SERVICE, pacProxyService);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
reportWtf("starting PacProxyService", e);
|
||||||
|
}
|
||||||
|
t.traceEnd();
|
||||||
|
|
||||||
t.traceBegin("StartConnectivityService");
|
t.traceBegin("StartConnectivityService");
|
||||||
// This has to be called after NetworkManagementService, NetworkStatsService
|
// This has to be called after NetworkManagementService, NetworkStatsService
|
||||||
// and NetworkPolicyManager because ConnectivityService needs to take these
|
// and NetworkPolicyManager because ConnectivityService needs to take these
|
||||||
|
|||||||
Reference in New Issue
Block a user