0%

adb网络代理原理

设置代理

  • 设置代理

adb shell settings put global http_proxy 127.0.0.1:8888

  • 移除代理

adb shell settings delete global http_proxy

adb shell settings delete global global_http_proxy_host

adb shell settings delete global global_http_proxy_port

adb reboot //部分手机必须重启

  • 脚本设置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    if [ "$1" == "proxy" ];then
    if [ "$2"x == "delete"x ];then
    adb shell settings delete global http_proxy
    adb shell settings delete global global_http_proxy_host
    adb shell settings delete global global_http_proxy_port
    adb reboot
    else
    ip=$(ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:")
    # echo "本机ip为:$ip"
    adb shell settings put global http_proxy $ip:8888
    exit;
    fi
    fi

手机目录

settings_global目录
/data/system/users/0/settings_global.xml

1
2
3
4
5
6
7
8
9
10
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<settings version="191">
...
<setting id="282" name="global_http_proxy_host" value="30.28.56.60" package="android" defaultValue="30.28.56.60" defaultSysSet="true" />
<setting id="283" name="global_http_proxy_port" value="8888" package="android" defaultValue="8888" defaultSysSet="true" />
<setting id="284" name="global_http_proxy_exclusion_list" value="" package="android" defaultValue="" defaultSysSet="true" />
<setting id="281" name="http_proxy" value="30.28.56.60:8888" package="root" />
...
</settings>
<namespaceHashes />

cmd/settings 目录
/system/bin/settings
/system/bin/cmd ELF

源码解析

原理: adb shell settings 做了什么????

  • settings (/aosp/frameworks/base/cmds/settings)

    1
    2
    #!/system/bin/sh
    cmd settings "$@" //执行cmd "$@"为传递给脚本的参数
  • cmd (/aosp/frameworks/native/cmds/cmd/cmd.cpp) 和 binder (/aosp/frameworks/native/libs/binder/Binder.cpp)

cmd在手机上是elf可执行文件,原理是通过settings和”$@”为作为参数传递给cmd,cmd中通过”settings”字符串通过ServiceManager获取Binder,然后列用binder调用对应的Settings功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, TextOutput& errorLog,
int in, int out, int err, RunMode runMode) {
sp<ProcessState> proc = ProcessState::self();
proc->startThreadPool();

#if DEBUG
ALOGD("cmd: starting");
#endif
sp<IServiceManager> sm = defaultServiceManager(); // ServiceManager
if (runMode == RunMode::kStandalone) {
fflush(stdout);
}
if (sm == nullptr) {
ALOGW("Unable to get default service manager!");
errorLog << "cmd: Unable to get default service manager!" << endl;
return 20;
}

int argc = argv.size();

if (argc == 0) {
errorLog << "cmd: No service specified; use -l to list all running services. Use -w to start and wait for a service." << endl;
return 20;
}

//adb shell cmd -l 命令,列出了cmd可用的服务

if ((argc == 1) && (argv[0] == "-l")) {
Vector<String16> services = sm->listServices(); //ServiceManager中的方法
services.sort(sort_func);
outputLog << "Currently running services:" << endl;

for (size_t i=0; i<services.size(); i++) {
sp<IBinder> service = sm->checkService(services[i]);
if (service != nullptr) {
outputLog << " " << services[i] << endl;
}
}
return 0;
}

bool waitForService = ((argc > 1) && (argv[0] == "-w"));
int serviceIdx = (waitForService) ? 1 : 0;
const auto cmd = argv[serviceIdx];

Vector<String16> args;
String16 serviceName = String16(cmd.data(), cmd.size());
for (int i = serviceIdx + 1; i < argc; i++) {
args.add(String16(argv[i].data(), argv[i].size()));
}
sp<IBinder> service;
if(waitForService) {
service = sm->waitForService(serviceName); //ServiceManager中的方法
} else {
service = sm->checkService(serviceName); //ServiceManager中的方法
}

if (service == nullptr) {
if (runMode == RunMode::kStandalone) {
ALOGW("Can't find service %.*s", static_cast<int>(cmd.size()), cmd.data());
}
errorLog << "cmd: Can't find service: " << cmd << endl;
return 20;
}

sp<MyShellCallback> cb = new MyShellCallback(errorLog);
sp<MyResultReceiver> result = new MyResultReceiver();

#if DEBUG
ALOGD("cmd: Invoking %.*s in=%d, out=%d, err=%d",
static_cast<int>(cmd.size()), cmd.data(), in, out, err);
#endif

// TODO: block until a result is returned to MyResultReceiver.
status_t error = IBinder::shellCommand(service, in, out, err, args, cb, result); //执行binder
if (error < 0) {
const char* errstr;
switch (error) {
case BAD_TYPE: errstr = "Bad type"; break;
case FAILED_TRANSACTION: errstr = "Failed transaction"; break;
case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break;
case UNEXPECTED_NULL: errstr = "Unexpected null"; break;
default: errstr = strerror(-error); break;
}
if (runMode == RunMode::kStandalone) {
ALOGW("Failure calling service %.*s: %s (%d)", static_cast<int>(cmd.size()), cmd.data(),
errstr, -error);
}
outputLog << "cmd: Failure calling service " << cmd << ": " << errstr << " (" << (-error)
<< ")" << endl;
return error;
}

cb->mActive = false;
status_t res = result->waitForResult();
#if DEBUG
ALOGD("result=%d", (int)res);
#endif
return res;
}


/**
* native
* SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'),
* java
* int SHELL_COMMAND_TRANSACTION = ('_'<<24)|('C'<<16)|('M'<<8)|'D';
*/

/aosp/frameworks/native/libs/binder/Binder.cpp

status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int err,
Vector<String16>& args, const sp<IShellCallback>& callback,
const sp<IResultReceiver>& resultReceiver)
{
Parcel send;
Parcel reply;
send.writeFileDescriptor(in);
send.writeFileDescriptor(out);
send.writeFileDescriptor(err);
const size_t numArgs = args.size();
send.writeInt32(numArgs);
for (size_t i = 0; i < numArgs; i++) {
send.writeString16(args[i]);
}
send.writeStrongBinder(callback != nullptr ? IInterface::asBinder(callback) : nullptr);
send.writeStrongBinder(resultReceiver != nullptr ? IInterface::asBinder(resultReceiver) : nullptr);
return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply);
}
  • ServiceManager.java

ServiceManager作为一个系统服务中心,是一个匿名binder的机制,通俗讲就是通过binder调用binder机制,@hide api

  • IBinder getService(String name)

  • void addService(String name, IBinder service) // 下方代码中会调用addService(“settings”,xxxx);注册settings服务

  • IBinder checkService(String name)

  • IBinder waitForService(String name)

  • String[] listServices()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    public final class ServiceManager {
    private static final String TAG = "ServiceManager";
    private static final Object sLock = new Object();

    @UnsupportedAppUsage
    private static IServiceManager sServiceManager;

    /**
    * Cache for the "well known" services, such as WM and AM.
    */
    @UnsupportedAppUsage
    private static Map<String, IBinder> sCache = new ArrayMap<String, IBinder>();

    @UnsupportedAppUsage
    public ServiceManager() {
    }

    @UnsupportedAppUsage
    private static IServiceManager getIServiceManager() {
    if (sServiceManager != null) {
    return sServiceManager;
    }

    // Find the service manager
    sServiceManager = ServiceManagerNative
    .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
    return sServiceManager;
    }

    public static IBinder getService(String name) {
    try {
    IBinder service = sCache.get(name);
    if (service != null) {
    return service;
    } else {
    return Binder.allowBlocking(rawGetService(name));
    }
    } catch (RemoteException e) {
    Log.e(TAG, "error in getService", e);
    }
    return null;
    }
    /**
    * 下面SettingsProvider 会进行注册
    */
    public static void addService(String name, IBinder service) {
    addService(name, service, false, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
    }


    public static void addService(String name, IBinder service, boolean allowIsolated) {
    addService(name, service, allowIsolated, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
    }

    public static void addService(String name, IBinder service, boolean allowIsolated,
    int dumpPriority) {
    try {
    getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
    } catch (RemoteException e) {
    Log.e(TAG, "error in addService", e);
    }
    }

    /**
    * 上面的checkService
    */
    public static IBinder checkService(String name) {
    try {
    IBinder service = sCache.get(name);
    if (service != null) {
    return service;
    } else {
    return Binder.allowBlocking(getIServiceManager().checkService(name));
    }
    } catch (RemoteException e) {
    Log.e(TAG, "error in checkService", e);
    return null;
    }
    }



    /**
    * 上面的waitForService
    */
    public static native IBinder waitForService(@NonNull String name);



    /**
    * 上面的listServices
    */
    @UnsupportedAppUsage
    public static String[] listServices() {
    try {
    return getIServiceManager().listServices(IServiceManager.DUMP_FLAG_PRIORITY_ALL);
    } catch (RemoteException e) {
    Log.e(TAG, "error in listServices", e);
    return null;
    }
    }

    private static IBinder rawGetService(String name) throws RemoteException {
    final long start = sStatLogger.getTime();

    final IBinder binder = getIServiceManager().getService(name);

    final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);

    final int myUid = Process.myUid();
    final boolean isCore = UserHandle.isCore(myUid);

    final long slowThreshold = isCore
    ? GET_SERVICE_SLOW_THRESHOLD_US_CORE
    : GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE;

    synchronized (sLock) {
    sGetServiceAccumulatedUs += time;
    sGetServiceAccumulatedCallCount++;

    final long nowUptime = SystemClock.uptimeMillis();

    // Was a slow call?
    if (time >= slowThreshold) {
    // We do a slow log:
    // - At most once in every SLOW_LOG_INTERVAL_MS
    // - OR it was slower than the previously logged slow call.
    if ((nowUptime > (sLastSlowLogUptime + SLOW_LOG_INTERVAL_MS))
    || (sLastSlowLogActualTime < time)) {
    EventLogTags.writeServiceManagerSlow(time / 1000, name);

    sLastSlowLogUptime = nowUptime;
    sLastSlowLogActualTime = time;
    }
    }

    // Every GET_SERVICE_LOG_EVERY_CALLS calls, log the total time spent in getService().

    final int logInterval = isCore
    ? GET_SERVICE_LOG_EVERY_CALLS_CORE
    : GET_SERVICE_LOG_EVERY_CALLS_NON_CORE;

    if ((sGetServiceAccumulatedCallCount >= logInterval)
    && (nowUptime >= (sLastStatsLogUptime + STATS_LOG_INTERVAL_MS))) {

    EventLogTags.writeServiceManagerStats(
    sGetServiceAccumulatedCallCount, // Total # of getService() calls.
    sGetServiceAccumulatedUs / 1000, // Total time spent in getService() calls.
    (int) (nowUptime - sLastStatsLogUptime)); // Uptime duration since last log.
    sGetServiceAccumulatedCallCount = 0;
    sGetServiceAccumulatedUs = 0;
    sLastStatsLogUptime = nowUptime;
    }
    }
    return binder;
    }
    }

  • SettingsService.java

settings对应的binder对象

  • native端通过binder调用 onTransact 调用到java端实体binder (code = SHELL_COMMAND_TRANSACTION)

  • SHELL_COMMAND_TRANSACTION为binder内部的方法

  • shellCommand(Binder) - > onShellCommand(SettingsService)

  • exec(BasicShellCommandHandler) -> onCommand(SettingsService.MyShellCommand)

  • 解析args并执行

  • eg:put -> putForUser -> call(IContentProvider,method=[PUT_global]) ->

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    final public class SettingsService extends Binder {
    final SettingsProvider mProvider;

    public SettingsService(SettingsProvider provider) {
    mProvider = provider;
    }

    @Override
    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
    String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
    (new MyShellCommand(mProvider, false)).exec(
    this, in, out, err, args, callback, resultReceiver);
    }

    @Override
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {

    }

    final static class MyShellCommand extends ShellCommand {
    final SettingsProvider mProvider;
    final boolean mDumping;

    enum CommandVerb {
    UNSPECIFIED,
    GET,
    PUT,
    DELETE,
    LIST,
    RESET,
    }

    int mUser = UserHandle.USER_NULL;
    CommandVerb mVerb = CommandVerb.UNSPECIFIED;
    String mTable = null;
    String mKey = null;
    String mValue = null;
    String mPackageName = null;
    String mTag = null;
    int mResetMode = -1;
    boolean mMakeDefault;

    MyShellCommand(SettingsProvider provider, boolean dumping) {
    mProvider = provider;
    mDumping = dumping;
    }

    @Override
    public int onCommand(String cmd) {
    if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) {
    return handleDefaultCommands(cmd);
    }

    final PrintWriter perr = getErrPrintWriter();

    boolean valid = false;
    String arg = cmd;
    do {
    if ("--user".equals(arg)) {
    if (mUser != UserHandle.USER_NULL) {
    perr.println("Invalid user: --user specified more than once");
    break;
    }
    mUser = UserHandle.parseUserArg(getNextArgRequired());

    if (mUser == UserHandle.USER_ALL) {
    perr.println("Invalid user: all");
    return -1;
    }
    } else if (mVerb == CommandVerb.UNSPECIFIED) {
    if ("get".equalsIgnoreCase(arg)) {
    mVerb = CommandVerb.GET;
    } else if ("put".equalsIgnoreCase(arg)) {
    mVerb = CommandVerb.PUT;
    } else if ("delete".equalsIgnoreCase(arg)) {
    mVerb = CommandVerb.DELETE;
    } else if ("list".equalsIgnoreCase(arg)) {
    mVerb = CommandVerb.LIST;
    } else if ("reset".equalsIgnoreCase(arg)) {
    mVerb = CommandVerb.RESET;
    } else {
    // invalid
    perr.println("Invalid command: " + arg);
    return -1;
    }
    } else if (mTable == null) {
    if (!"system".equalsIgnoreCase(arg)
    && !"secure".equalsIgnoreCase(arg)
    && !"global".equalsIgnoreCase(arg)) {
    perr.println("Invalid namespace '" + arg + "'");
    return -1;
    }
    mTable = arg.toLowerCase();
    if (mVerb == CommandVerb.LIST) {
    valid = true;
    break;
    }
    } else if (mVerb == CommandVerb.RESET) {
    if ("untrusted_defaults".equalsIgnoreCase(arg)) {
    mResetMode = Settings.RESET_MODE_UNTRUSTED_DEFAULTS;
    } else if ("untrusted_clear".equalsIgnoreCase(arg)) {
    mResetMode = Settings.RESET_MODE_UNTRUSTED_CHANGES;
    } else if ("trusted_defaults".equalsIgnoreCase(arg)) {
    mResetMode = Settings.RESET_MODE_TRUSTED_DEFAULTS;
    } else {
    mPackageName = arg;
    mResetMode = Settings.RESET_MODE_PACKAGE_DEFAULTS;
    if (peekNextArg() == null) {
    valid = true;
    } else {
    mTag = getNextArg();
    if (peekNextArg() == null) {
    valid = true;
    } else {
    perr.println("Too many arguments");
    return -1;
    }
    }
    break;
    }
    if (peekNextArg() == null) {
    valid = true;
    } else {
    perr.println("Too many arguments");
    return -1;
    }
    } else if (mVerb == CommandVerb.GET || mVerb == CommandVerb.DELETE) {
    mKey = arg;
    if (peekNextArg() == null) {
    valid = true;
    } else {
    perr.println("Too many arguments");
    return -1;
    }
    break;
    } else if (mKey == null) {
    mKey = arg;
    // keep going; there's another PUT arg
    } else if (mValue == null) {
    mValue = arg;
    // what we have so far is a valid command
    valid = true;
    // keep going; there may be another PUT arg
    } else if (mTag == null) {
    mTag = arg;
    if ("default".equalsIgnoreCase(mTag)) {
    mTag = null;
    mMakeDefault = true;
    if (peekNextArg() == null) {
    valid = true;
    } else {
    perr.println("Too many arguments");
    return -1;
    }
    break;
    }
    if (peekNextArg() == null) {
    valid = true;
    break;
    }
    } else { // PUT, final arg
    if (!"default".equalsIgnoreCase(arg)) {
    perr.println("Argument expected to be 'default'");
    return -1;
    }
    mMakeDefault = true;
    if (peekNextArg() == null) {
    valid = true;
    } else {
    perr.println("Too many arguments");
    return -1;
    }
    break;
    }
    } while ((arg = getNextArg()) != null);

    if (!valid) {
    perr.println("Bad arguments");
    return -1;
    }

    if (mUser == UserHandle.USER_NULL || mUser == UserHandle.USER_CURRENT) {
    try {
    mUser = ActivityManager.getService().getCurrentUser().id;
    } catch (RemoteException e) {
    throw new RuntimeException("Failed in IPC", e);
    }
    }
    UserManager userManager = UserManager.get(mProvider.getContext());
    if (userManager.getUserInfo(mUser) == null) {
    perr.println("Invalid user: " + mUser);
    return -1;
    }

    final IContentProvider iprovider = mProvider.getIContentProvider();
    final PrintWriter pout = getOutPrintWriter();
    switch (mVerb) {
    case GET:
    pout.println(getForUser(iprovider, mUser, mTable, mKey));
    break;
    case PUT:
    putForUser(iprovider, mUser, mTable, mKey, mValue, mTag, mMakeDefault);
    break;
    case DELETE:
    pout.println("Deleted "
    + deleteForUser(iprovider, mUser, mTable, mKey) + " rows");
    break;
    case LIST:
    for (String line : listForUser(iprovider, mUser, mTable)) {
    pout.println(line);
    }
    break;
    case RESET:
    resetForUser(iprovider, mUser, mTable, mTag);
    break;
    default:
    perr.println("Unspecified command");
    return -1;
    }

    return 0;
    }

    List<String> listForUser(IContentProvider provider, int userHandle, String table) {
    final String callListCommand;
    if ("system".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_SYSTEM;
    else if ("secure".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_SECURE;
    else if ("global".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_GLOBAL;
    else {
    getErrPrintWriter().println("Invalid table; no list performed");
    throw new IllegalArgumentException("Invalid table " + table);
    }
    final ArrayList<String> lines = new ArrayList<String>();
    try {
    Bundle arg = new Bundle();
    arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
    Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
    callListCommand, null, arg);
    lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST));
    Collections.sort(lines);
    } catch (RemoteException e) {
    throw new RuntimeException("Failed in IPC", e);
    }
    return lines;
    }

    String getForUser(IContentProvider provider, int userHandle,
    final String table, final String key) {
    final String callGetCommand;
    if ("system".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SYSTEM;
    else if ("secure".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SECURE;
    else if ("global".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_GLOBAL;
    else {
    getErrPrintWriter().println("Invalid table; no put performed");
    throw new IllegalArgumentException("Invalid table " + table);
    }

    String result = null;
    try {
    Bundle arg = new Bundle();
    arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
    Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
    callGetCommand, key, arg);
    if (b != null) {
    result = b.getPairValue();
    }
    } catch (RemoteException e) {
    throw new RuntimeException("Failed in IPC", e);
    }
    return result;
    }

    void putForUser(IContentProvider provider, int userHandle, final String table,
    final String key, final String value, String tag, boolean makeDefault) {
    final String callPutCommand;
    if ("system".equals(table)) {
    callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM;
    if (makeDefault) {
    getOutPrintWriter().print("Ignored makeDefault - "
    + "doesn't apply to system settings");
    makeDefault = false;
    }
    } else if ("secure".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SECURE;
    else if ("global".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_GLOBAL;
    //public static final String CALL_METHOD_PUT_GLOBAL= "PUT_global";

    else {
    getErrPrintWriter().println("Invalid table; no put performed");
    return;
    }

    try {
    Bundle arg = new Bundle();
    arg.putString(Settings.NameValueTable.VALUE, value);
    arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
    if (tag != null) {
    arg.putString(Settings.CALL_METHOD_TAG_KEY, tag);
    }
    if (makeDefault) {
    arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
    }
    provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
    callPutCommand, key, arg);
    } catch (RemoteException e) {
    throw new RuntimeException("Failed in IPC", e);
    }
    }

    int deleteForUser(IContentProvider provider, int userHandle,
    final String table, final String key) {
    final String callDeleteCommand;
    if ("system".equals(table)) {
    callDeleteCommand = Settings.CALL_METHOD_DELETE_SYSTEM;
    } else if ("secure".equals(table)) {
    callDeleteCommand = Settings.CALL_METHOD_DELETE_SECURE;
    } else if ("global".equals(table)) {
    callDeleteCommand = Settings.CALL_METHOD_DELETE_GLOBAL;
    } else {
    getErrPrintWriter().println("Invalid table; no delete performed");
    throw new IllegalArgumentException("Invalid table " + table);
    }

    try {
    Bundle arg = new Bundle();
    arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
    Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
    callDeleteCommand, key, arg);
    return result.getInt(SettingsProvider.RESULT_ROWS_DELETED);
    } catch (RemoteException e) {
    throw new RuntimeException("Failed in IPC", e);
    }
    }

    void resetForUser(IContentProvider provider, int userHandle,
    String table, String tag) {
    final String callResetCommand;
    if ("secure".equals(table)) callResetCommand = Settings.CALL_METHOD_RESET_SECURE;
    else if ("global".equals(table)) callResetCommand = Settings.CALL_METHOD_RESET_GLOBAL;
    else {
    getErrPrintWriter().println("Invalid table; no reset performed");
    return;
    }

    try {
    Bundle arg = new Bundle();
    arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
    arg.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, mResetMode);
    if (tag != null) {
    arg.putString(Settings.CALL_METHOD_TAG_KEY, tag);
    }
    String packageName = mPackageName != null ? mPackageName : resolveCallingPackage();
    arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
    provider.call(packageName, null, Settings.AUTHORITY, callResetCommand, null, arg);
    } catch (RemoteException e) {
    throw new RuntimeException("Failed in IPC", e);
    }
    }

    public static String resolveCallingPackage() {
    switch (Binder.getCallingUid()) {
    case Process.ROOT_UID: {
    return "root";
    }

    case Process.SHELL_UID: {
    return "com.android.shell";
    }

    default: {
    return null;
    }
    }
    }

    @Override
    public void onHelp() {
    PrintWriter pw = getOutPrintWriter();
    dumpHelp(pw, mDumping);
    }

    static void dumpHelp(PrintWriter pw, boolean dumping) {
    if (dumping) {
    pw.println("Settings provider dump options:");
    pw.println(" [-h] [--proto]");
    pw.println(" -h: print this help.");
    pw.println(" --proto: dump as protobuf.");
    } else {
    pw.println("Settings provider (settings) commands:");
    pw.println(" help");
    pw.println(" Print this help text.");
    pw.println(" get [--user <USER_ID> | current] NAMESPACE KEY");
    pw.println(" Retrieve the current value of KEY.");
    pw.println(" put [--user <USER_ID> | current] NAMESPACE KEY VALUE [TAG] [default]");
    pw.println(" Change the contents of KEY to VALUE.");
    pw.println(" TAG to associate with the setting.");
    pw.println(" {default} to set as the default, case-insensitive only for global/secure namespace");
    pw.println(" delete [--user <USER_ID> | current] NAMESPACE KEY");
    pw.println(" Delete the entry for KEY.");
    pw.println(" reset [--user <USER_ID> | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}");
    pw.println(" Reset the global/secure table for a package with mode.");
    pw.println(" RESET_MODE is one of {untrusted_defaults, untrusted_clear, trusted_defaults}, case-insensitive");
    pw.println(" list [--user <USER_ID> | current] NAMESPACE");
    pw.println(" Print all defined keys.");
    pw.println(" NAMESPACE is one of {system, secure, global}, case-insensitive");
    }
    }
    }
    }
  • Settings.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public final class Settings {
    /**
    * Host name and port for global http proxy. Uses ':' seperator for
    * between host and port.
    */
    public static final String HTTP_PROXY = "http_proxy";

    /**
    * Host name for global http proxy. Set via ConnectivityManager.
    *
    * @hide
    */
    public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";

    /**
    * Integer host port for global http proxy. Set via ConnectivityManager.
    *
    * @hide
    */
    public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
    }
  • SettingsProvider.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    public class SettingsProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
    Settings.setInSystemServer();

    synchronized (mLock) {
    mUserManager = UserManager.get(getContext());
    mPackageManager = AppGlobals.getPackageManager();
    mHandlerThread = new HandlerThread(LOG_TAG,
    Process.THREAD_PRIORITY_BACKGROUND);
    mHandlerThread.start();
    mHandler = new Handler(mHandlerThread.getLooper());
    mSettingsRegistry = new SettingsRegistry();
    }
    mHandler.post(() -> {
    registerBroadcastReceivers();
    startWatchingUserRestrictionChanges();
    });
    ServiceManager.addService("settings", new SettingsService(this)); //注册 settings 服务
    ServiceManager.addService("device_config", new DeviceConfigService(this));
    return true;
    }

    @Override
    public Bundle call(String method, String name, Bundle args) {
    final int requestingUserId = getRequestingUserId(args);
    switch (method) {
    case Settings.CALL_METHOD_PUT_GLOBAL: {
    String value = getSettingValue(args);
    String tag = getSettingTag(args);
    final boolean makeDefault = getSettingMakeDefault(args);
    final boolean overrideableByRestore = getSettingOverrideableByRestore(args);
    insertGlobalSetting(name, value, tag, makeDefault, requestingUserId, false,
    overrideableByRestore);
    break;
    }
    }
    }

    final class SettingsRegistry {
    private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";

    private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml"; //代理目录

    private File getSettingsFile(int key) {
    if (isConfigSettingsKey(key)) {
    final int userId = getUserIdFromKey(key);
    return new File(Environment.getUserSystemDirectory(userId),
    SETTINGS_FILE_CONFIG);
    } else if (isGlobalSettingsKey(key)) {
    final int userId = getUserIdFromKey(key);
    return new File(Environment.getUserSystemDirectory(userId),
    SETTINGS_FILE_GLOBAL);
    } else {
    throw new IllegalArgumentException("Invalid settings key:" + key);
    }
    }
    }
    }
  • adb shell cmd -l

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    ➜  ~ adb shell cmd -l
    Currently running services:
    DockObserver
    SurfaceFlinger
    accessibility
    account
    activity
    activity_task
    adb
    alarm
    android.hardware.identity.IIdentityCredentialStore/default
    android.hardware.power.IPower/default
    android.hardware.rebootescrow.IRebootEscrow/default
    android.hardware.vibrator.IVibrator/default
    android.os.UpdateEngineService
    android.security.identity
    android.security.keystore
    android.service.gatekeeper.IGateKeeperService
    app_binding
    app_integrity
    appops
    appwidget
    audio
    auth
    autofill
    backup
    battery
    batteryproperties
    batterystats
    binder_calls_stats
    biometric
    blob_store
    bluetooth_manager
    bugreport
    cacheinfo
    carrier_config
    clipboard
    color_display
    companiondevice
    connectivity
    connmetrics
    consumer_ir
    content
    contexthub
    country_detector
    cpuinfo
    crossprofileapps
    dataloader_manager
    dbinfo
    device_config
    device_identifiers
    device_policy
    deviceidle
    devicestoragemonitor
    diskstats
    display
    dnsresolver
    dreams
    drm.drmManager
    dropbox
    dynamic_system
    emergency_affordance
    ethernet
    external_vibrator_service
    face
    file_integrity
    gfxinfo
    gpu
    graphicsstats
    hardware_properties
    imms
    incident
    incidentcompanion
    incremental
    input
    input_method
    inputflinger
    installd
    ions
    iorapd
    iphonesubinfo
    ipsec
    isms
    isub
    jobscheduler
    launcherapps
    lights
    location
    lock_settings
    looper_stats
    manager
    media.aaudio
    media.audio_flinger
    media.audio_policy
    media.camera
    media.camera.proxy
    media.metrics
    media.player
    media.resource_manager
    media_projection
    media_resource_monitor
    media_router
    media_session
    meminfo
    midi
    mount
    netd
    netd_listener
    netpolicy
    netstats
    network_management
    network_score
    network_stack
    network_time_update_service
    network_watchlist
    nfc
    notification
    oem_lock
    otadexopt
    overlay
    package
    package_native
    permission
    permissionmgr
    persistent_data_block
    phone
    pinner
    platform_compat
    platform_compat_native
    power
    print
    processinfo
    procstats
    qchook
    rcs
    recovery
    restrictions
    role
    rollback
    runtime
    scheduling_policy
    search
    sec_key_att_app_id_provider
    secure_element
    sensor_privacy
    sensorservice
    serial
    servicediscovery
    settings
    shortcut
    simphonebook
    sip
    slice
    soundtrigger
    soundtrigger_middleware
    stats
    statscompanion
    statsmanager
    statusbar
    storaged
    storaged_pri
    storagestats
    suspend_control
    system_config
    system_update
    telecom
    telephony.registry
    telephony_ims
    testharness
    tethering
    textclassification
    textservices
    thermalservice
    time_detector
    time_zone_detector
    trust
    uimode
    updatelock
    uri_grants
    usagestats
    usb
    user
    vibrator
    voiceinteraction
    vold
    wallpaper
    webviewupdate
    wifi
    wifiaware
    wifinl80211
    wifip2p
    wifirtt
    wifiscanner
    window
  • 其他代理方式

    • 直接调用SettingsProvider
    • 设备管理权限设置
    • 参考:com.android.server.connectivity.ProxyTracker
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      184
      185
      186
      187
      188
      189
      190
      191
      192
      193
      194
      195
      196
      197
      198
      199
      200
      201
      202
      203
      204
      205
      206
      207
      208
      209
      210
      211
      212
      213
      214
      215
      216
      217
      218
      219
      220
      221
      222
      223
      224
      225
      226
      227
      228
      229
      230
      231
      232
      233
      234
      235
      236
      237
      238
      239
      240
      241
      242
      243
      244
      245
      246
      247
      248
      249
      250
      251
      252
      253
      254
      255
      256
      257
      258
      259
      260
      261
      262
      263
      264
      265
      266
      267
      268
      269
      270
      271
      272
      273
      274
      275
      276
      277
      278
      279
      280
      281
      282
      283
      284
      285
      286
      287
      288
      289
      290
      291
      292
      293
      294
      295
      296
      297
      298
      299
      300
      301
      302
      303
      304
      305
      306
      307
      308
      309
      310
      311
      312
      /**
      * Copyright (c) 2018, The Android Open Source Project
      *
      * Licensed under the Apache License, Version 2.0 (the "License");
      * you may not use this file except in compliance with the License.
      * You may obtain a copy of the License at
      *
      * http://www.apache.org/licenses/LICENSE-2.0
      *
      * Unless required by applicable law or agreed to in writing, software
      * distributed under the License is distributed on an "AS IS" BASIS,
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */

      package com.android.server.connectivity;

      import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST;
      import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_HOST;
      import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PAC;
      import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PORT;
      import static android.provider.Settings.Global.HTTP_PROXY;

      import android.annotation.NonNull;
      import android.annotation.Nullable;
      import android.content.ContentResolver;
      import android.content.Context;
      import android.content.Intent;
      import android.net.Proxy;
      import android.net.ProxyInfo;
      import android.net.Uri;
      import android.os.Binder;
      import android.os.Handler;
      import android.os.UserHandle;
      import android.provider.Settings;
      import android.text.TextUtils;
      import android.util.Slog;

      import com.android.internal.annotations.GuardedBy;

      import java.util.Objects;

      /**
      * A class to handle proxy for ConnectivityService.
      *
      * @hide
      */
      public class ProxyTracker {
      private static final String TAG = ProxyTracker.class.getSimpleName();
      private static final boolean DBG = true;

      @NonNull
      private final Context mContext;

      @NonNull
      private final Object mProxyLock = new Object();
      // The global proxy is the proxy that is set device-wide, overriding any network-specific
      // proxy. Note however that proxies are hints ; the system does not enforce their use. Hence
      // this value is only for querying.
      @Nullable
      @GuardedBy("mProxyLock")
      private ProxyInfo mGlobalProxy = null;
      // The default proxy is the proxy that applies to no particular network if the global proxy
      // 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
      // in its LinkProperties, or when ConnectivityService switches to a new default network, or
      // when PacManager resolves the proxy.
      @Nullable
      @GuardedBy("mProxyLock")
      private volatile ProxyInfo mDefaultProxy = null;
      // Whether the default proxy is enabled.
      @GuardedBy("mProxyLock")
      private boolean mDefaultProxyEnabled = true;

      // The object responsible for Proxy Auto Configuration (PAC).
      @NonNull
      private final PacManager mPacManager;

      public ProxyTracker(@NonNull final Context context,
      @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
      mContext = context;
      mPacManager = new PacManager(context, connectivityServiceInternalHandler, pacChangedEvent);
      }

      // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
      // (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific
      // proxy is null then there is no proxy in place).
      @Nullable
      private static ProxyInfo canonicalizeProxyInfo(@Nullable final ProxyInfo proxy) {
      if (proxy != null && TextUtils.isEmpty(proxy.getHost())
      && Uri.EMPTY.equals(proxy.getPacFileUrl())) {
      return null;
      }
      return proxy;
      }

      // ProxyInfo equality functions with a couple modifications over ProxyInfo.equals() to make it
      // better for determining if a new proxy broadcast is necessary:
      // 1. Canonicalize empty ProxyInfos to null so an empty proxy compares equal to null so as to
      // avoid unnecessary broadcasts.
      // 2. Make sure all parts of the ProxyInfo's compare true, including the host when a PAC URL
      // is in place. This is important so legacy PAC resolver (see com.android.proxyhandler)
      // changes aren't missed. The legacy PAC resolver pretends to be a simple HTTP proxy but
      // actually uses the PAC to resolve; this results in ProxyInfo's with PAC URL, host and port
      // all set.
      public static boolean proxyInfoEqual(@Nullable final ProxyInfo a, @Nullable final ProxyInfo b) {
      final ProxyInfo pa = canonicalizeProxyInfo(a);
      final ProxyInfo pb = canonicalizeProxyInfo(b);
      // ProxyInfo.equals() doesn't check hosts when PAC URLs are present, but we need to check
      // hosts even when PAC URLs are present to account for the legacy PAC resolver.
      return Objects.equals(pa, pb) && (pa == null || Objects.equals(pa.getHost(), pb.getHost()));
      }

      /**
      * Gets the default system-wide proxy.
      *
      * This will return the global proxy if set, otherwise the default proxy if in use. Note
      * that this is not necessarily the proxy that any given process should use, as the right
      * proxy for a process is the proxy for the network this process will use, which may be
      * different from this value. This value is simply the default in case there is no proxy set
      * in the network that will be used by a specific process.
      * @return The default system-wide proxy or null if none.
      */
      @Nullable
      public ProxyInfo getDefaultProxy() {
      // This information is already available as a world read/writable jvm property.
      synchronized (mProxyLock) {
      if (mGlobalProxy != null) return mGlobalProxy;
      if (mDefaultProxyEnabled) return mDefaultProxy;
      return null;
      }
      }

      /**
      * Gets the global proxy.
      *
      * @return The global proxy or null if none.
      */
      @Nullable
      public ProxyInfo getGlobalProxy() {
      // This information is already available as a world read/writable jvm property.
      synchronized (mProxyLock) {
      return mGlobalProxy;
      }
      }

      /**
      * Read the global proxy settings and cache them in memory.
      */
      public void loadGlobalProxy() {
      ContentResolver res = mContext.getContentResolver();
      String host = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_HOST);
      int port = Settings.Global.getInt(res, GLOBAL_HTTP_PROXY_PORT, 0);
      String exclList = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
      String pacFileUrl = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_PAC);
      if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
      ProxyInfo proxyProperties;
      if (!TextUtils.isEmpty(pacFileUrl)) {
      proxyProperties = new ProxyInfo(pacFileUrl);
      } else {
      proxyProperties = new ProxyInfo(host, port, exclList);
      }
      if (!proxyProperties.isValid()) {
      if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
      return;
      }

      synchronized (mProxyLock) {
      mGlobalProxy = proxyProperties;
      }
      }
      loadDeprecatedGlobalHttpProxy();
      // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ?
      }

      /**
      * Read the global proxy from the deprecated Settings.Global.HTTP_PROXY setting and apply it.
      */
      public void loadDeprecatedGlobalHttpProxy() {
      final String proxy = Settings.Global.getString(mContext.getContentResolver(), HTTP_PROXY);
      if (!TextUtils.isEmpty(proxy)) {
      String data[] = proxy.split(":");
      if (data.length == 0) {
      return;
      }

      final String proxyHost = data[0];
      int proxyPort = 8080;
      if (data.length > 1) {
      try {
      proxyPort = Integer.parseInt(data[1]);
      } catch (NumberFormatException e) {
      return;
      }
      }
      final ProxyInfo p = new ProxyInfo(proxyHost, proxyPort, "");
      setGlobalProxy(p);
      }
      }

      /**
      * Sends the system broadcast informing apps about a new proxy configuration.
      *
      * Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
      * to do in a "sendProxyBroadcast" method.
      */
      public void sendProxyBroadcast() {
      final ProxyInfo defaultProxy = getDefaultProxy();
      final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : new ProxyInfo("", 0, "");
      if (mPacManager.setCurrentProxyScriptUrl(proxyInfo) == PacManager.DONT_SEND_BROADCAST) {
      return;
      }
      if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
      Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
      intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
      Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
      intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxyInfo);
      final long ident = Binder.clearCallingIdentity();
      try {
      mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
      } finally {
      Binder.restoreCallingIdentity(ident);
      }
      }

      /**
      * Sets the global proxy in memory. Also writes the values to the global settings of the device.
      *
      * @param proxyInfo the proxy spec, or null for no proxy.
      */
      public void setGlobalProxy(@Nullable ProxyInfo proxyInfo) {
      synchronized (mProxyLock) {
      // ProxyInfo#equals is not commutative :( and is public API, so it can't be fixed.
      if (proxyInfo == mGlobalProxy) return;
      if (proxyInfo != null && proxyInfo.equals(mGlobalProxy)) return;
      if (mGlobalProxy != null && mGlobalProxy.equals(proxyInfo)) return;

      final String host;
      final int port;
      final String exclList;
      final String pacFileUrl;
      if (proxyInfo != null && (!TextUtils.isEmpty(proxyInfo.getHost()) ||
      !Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))) {
      if (!proxyInfo.isValid()) {
      if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
      return;
      }
      mGlobalProxy = new ProxyInfo(proxyInfo);
      host = mGlobalProxy.getHost();
      port = mGlobalProxy.getPort();
      exclList = mGlobalProxy.getExclusionListAsString();
      pacFileUrl = Uri.EMPTY.equals(proxyInfo.getPacFileUrl())
      ? "" : proxyInfo.getPacFileUrl().toString();
      } else {
      host = "";
      port = 0;
      exclList = "";
      pacFileUrl = "";
      mGlobalProxy = null;
      }
      final ContentResolver res = mContext.getContentResolver();
      final long token = Binder.clearCallingIdentity();
      try {
      Settings.Global.putString(res, GLOBAL_HTTP_PROXY_HOST, host); //设置host
      Settings.Global.putInt(res, GLOBAL_HTTP_PROXY_PORT, port); //设置端口
      Settings.Global.putString(res, GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclList);
      Settings.Global.putString(res, GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
      } finally {
      Binder.restoreCallingIdentity(token);
      }

      sendProxyBroadcast();
      }
      }

      /**
      * Sets the default proxy for the device.
      *
      * The default proxy is the proxy used for networks that do not have a specific proxy.
      * @param proxyInfo the proxy spec, or null for no proxy.
      */
      public void setDefaultProxy(@Nullable ProxyInfo proxyInfo) {
      synchronized (mProxyLock) {
      if (Objects.equals(mDefaultProxy, proxyInfo)) return;
      if (proxyInfo != null && !proxyInfo.isValid()) {
      if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
      return;
      }

      // This call could be coming from the PacManager, containing the port of 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.
      // TODO: Switch PacManager to have its own message to send back rather than
      // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
      if ((mGlobalProxy != null) && (proxyInfo != null)
      && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
      && proxyInfo.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
      mGlobalProxy = proxyInfo;
      sendProxyBroadcast();
      return;
      }
      mDefaultProxy = proxyInfo;

      if (mGlobalProxy != null) return;
      if (mDefaultProxyEnabled) {
      sendProxyBroadcast();
      }
      }
      }
      }