设置代理
- 设置代理
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
13if [ "$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 |
|
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 | int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, TextOutput& errorLog, |
- 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
158public final class ServiceManager {
private static final String TAG = "ServiceManager";
private static final Object sLock = new Object();
private static IServiceManager sServiceManager;
/**
* Cache for the "well known" services, such as WM and AM.
*/
private static Map<String, IBinder> sCache = new ArrayMap<String, IBinder>();
public ServiceManager() {
}
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( String name);
/**
* 上面的listServices
*/
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
408final public class SettingsService extends Binder {
final SettingsProvider mProvider;
public SettingsService(SettingsProvider provider) {
mProvider = provider;
}
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);
}
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;
}
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;
}
}
}
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
21public 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
59public class SettingsProvider extends ContentProvider {
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;
}
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;
private final Context mContext;
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.
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.
private volatile ProxyInfo mDefaultProxy = null;
// Whether the default proxy is enabled.
private boolean mDefaultProxyEnabled = true;
// The object responsible for Proxy Auto Configuration (PAC).
private final PacManager mPacManager;
public ProxyTracker(final Context context,
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).
private static ProxyInfo canonicalizeProxyInfo(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(final ProxyInfo a, 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.
*/
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.
*/
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( 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( 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();
}
}
}
}