具体现象:点击右上角的修改,然后修改wifi的密码,在隐私选项和网络是否按流量计费选项与原来不同时,就会发生密码修改失败。
现在开始具体的分析:
首先找到左图页面的xml文件,和对应的控制类。
packages/apps/Settings/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
packages/apps/Settings/res/xml/wifi_network_details_fragment2.xml
可以找到编辑按钮的创建和响应。在正常情况下走的是 showDialog(WIFI_DIALOG_ID);
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Log.d("xz", "onCreateOptionsMenu: ");
if (!mIsUiRestricted && isEditable()) {
MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.wifi_modify);
item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
Log.d("xz", "onOptionsItemSelected: menuItem.getItemId() = " + menuItem.getItemId());
Log.d("xz", "onOptionsItemSelected: canModifyNetwork() = " + mWifiDetailPreferenceController2.canModifyNetwork());
switch (menuItem.getItemId()) {
case Menu.FIRST:
if (!mWifiDetailPreferenceController2.canModifyNetwork()) {
EnforcedAdmin admin = RestrictedLockUtilsInternal.getDeviceOwner(getContext());
if (admin == null) {
final DevicePolicyManager dpm = (DevicePolicyManager)
getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
final UserManager um = (UserManager)
getContext().getSystemService(Context.USER_SERVICE);
final int profileOwnerUserId = Utils.getManagedProfileId(
um, UserHandle.myUserId());
if (profileOwnerUserId != UserHandle.USER_NULL) {
admin = new EnforcedAdmin(dpm.getProfileOwnerAsUser(profileOwnerUserId),
null, UserHandle.of(profileOwnerUserId));
}
}
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin);
} else {
Log.d("xz", "WifiNetworkDetailsFragment onOptionsItemSelected: showDialog(WIFI_DIALOG_ID)");
showDialog(WIFI_DIALOG_ID);
}
return true;
default:
return super.onOptionsItemSelected(menuItem);
}
}
showDialog()方法会调用到
packages/apps/Settings/src/com/android/settings/SettingsPreferenceFragment.java
的showDialog() 然后创建dialog
protected void showDialog(int dialogId) {
Log.d("xz", "SettingsPreferenceFragment showDialog: ");
if (mDialogFragment != null) {
Log.e(TAG, "Old dialog fragment not null!");
}
mDialogFragment = SettingsDialogFragment.newInstance(this, dialogId);
mDialogFragment.show(getChildFragmentManager(), Integer.toString(dialogId));
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Log.d("xz", " SettingsPreferenceFragment onCreateDialog: ");
if (savedInstanceState != null) {
mDialogId = savedInstanceState.getInt(KEY_DIALOG_ID, 0);
mParentFragment = getParentFragment();
int mParentFragmentId = savedInstanceState.getInt(KEY_PARENT_FRAGMENT_ID, -1);
if (mParentFragment == null) {
mParentFragment = getFragmentManager().findFragmentById(mParentFragmentId);
}
if (!(mParentFragment instanceof DialogCreatable)) {
throw new IllegalArgumentException(
(mParentFragment != null
? mParentFragment.getClass().getName()
: mParentFragmentId)
+ " must implement "
+ DialogCreatable.class.getName());
}
// This dialog fragment could be created from non-SettingsPreferenceFragment
if (mParentFragment instanceof SettingsPreferenceFragment) {
// restore mDialogFragment in mParentFragment
((SettingsPreferenceFragment) mParentFragment).mDialogFragment = this;
}
}
return ((DialogCreatable) mParentFragment).onCreateDialog(mDialogId);
}
可以看到最终调用了mParentFragment.onCreateDialog(mDialogId) ,此fragmengt就是WifiNetworkDetailsFragment。
所以又调用到了WifiNetworkDetailsFragment的onCreateDialog。
@Override
public Dialog onCreateDialog(int dialogId) {
Log.d("xz", "WifiNetworkDetailsFragment onCreateDialog: ");
if (getActivity() == null || mWifiDetailPreferenceController2 == null) {
return null;
}
final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
return WifiDialog2.createModal(getActivity(), this, wifiEntry,
WifiConfigUiBase2.MODE_MODIFY);
}
这里又调用了WifiDialog2.createModal()
public static WifiDialog2 createModal(Context context, WifiDialog2Listener listener,
WifiEntry wifiEntry, int mode) {
return new WifiDialog2(context, listener, wifiEntry, mode, 0 /* style */,
mode == WifiConfigUiBase2.MODE_VIEW /* hideSubmitButton */);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("xz", "WifiDialog2 onCreate: ");
setWindowsOverlay();
mView = getLayoutInflater().inflate(R.layout.wifi_dialog, /* root */ null);
setView(mView);
mController = new WifiConfigController2(this, mView, mWifiEntry, mMode);
super.onCreate(savedInstanceState);
if (mHideSubmitButton) {
mController.hideSubmitButton();
} else {
/* During creation, the submit button can be unavailable to determine
* visibility. Right after creation, update button visibility */
mController.enableSubmitIfAppropriate();
}
if (mWifiEntry == null) {
mController.hideForgetButton();
}
}
可以看出此dialog由 WifiConfigController2 进行管理。
在WifiConfigController2 的 initWifiConfigController2 的方法中设置了 保存按钮和监听。
private void initWifiConfigController2(WifiEntry wifiEntry, int mode) {
Log.d("xz", "initWifiConfigController2: start");
.............
.............
mConfigUi.setTitle(mWifiEntry.getTitle());
ViewGroup group = (ViewGroup) mView.findViewById(R.id.info);
...........
if (mMode == WifiConfigUiBase2.MODE_MODIFY) {
Log.d("xz", "initWifiConfigController2: setSubmitButton 1 ");
mConfigUi.setSubmitButton(res.getString(R.string.wifi_save));
这个mConfigUi 其实就是WifiDialog2类。setSubmitButton
@Override
public void setSubmitButton(CharSequence text) {
setButton(BUTTON_SUBMIT, text, this);
}
此方法会调用到frameworks/base/core/java/com/android/internal/app/AlertController.java
的setButton。
public void setButton(int whichButton, CharSequence text,
DialogInterface.OnClickListener listener, Message msg) {
if (msg == null && listener != null) {
msg = mHandler.obtainMessage(whichButton, listener);
}
switch (whichButton) {
case DialogInterface.BUTTON_POSITIVE:
mButtonPositiveText = text;
mButtonPositiveMessage = msg;
break;
case DialogInterface.BUTTON_NEGATIVE:
mButtonNegativeText = text;
mButtonNegativeMessage = msg;
break;
case DialogInterface.BUTTON_NEUTRAL:
mButtonNeutralText = text;
mButtonNeutralMessage = msg;
break;
default:
throw new IllegalArgumentException("Button does not exist");
}
}
这里走到的是第一个。msg = mHandler.obtainMessage(whichButton, listener);用handler设置了监听
private static final class ButtonHandler extends Handler {
// Button clicks have Message.what as the BUTTON{1,2,3} constant
private static final int MSG_DISMISS_DIALOG = 1;
private WeakReference<DialogInterface> mDialog;
public ButtonHandler(DialogInterface dialog) {
mDialog = new WeakReference<>(dialog);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DialogInterface.BUTTON_POSITIVE:
case DialogInterface.BUTTON_NEGATIVE:
case DialogInterface.BUTTON_NEUTRAL:
((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
break;
case MSG_DISMISS_DIALOG:
((DialogInterface) msg.obj).dismiss();
}
}
}
当点击保存按钮时就会执行((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
这里就会调用回到wifiDialog的onClick()方法去
@Override
public void onClick(DialogInterface dialogInterface, int id) {
Log.e("xz", "WifiDialog2 onClick: ",new Throwable());
if (mListener != null) {
switch (id) {
case BUTTON_SUBMIT:
mListener.onSubmit(this);
break;
case BUTTON_FORGET:
if (WifiUtils.isNetworkLockedDown(getContext(),
mWifiEntry.getWifiConfiguration())) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
RestrictedLockUtilsInternal.getDeviceOwner(getContext()));
return;
}
mListener.onForget(this);
break;
}
}
}
执行了mListener.onSubmit(this);
这个mListener。onSubmit就是WifiNetworkDetailsFragment类里去重写的。
@Override
public void onSubmit(WifiDialog2 dialog) {
for (WifiDialog2.WifiDialog2Listener listener : mWifiDialogListeners) {
listener.onSubmit(dialog);
}
}
这个listener.onSubmit是在此类初始化的。
mWifiDialogListeners.add(mWifiDetailPreferenceController2);
mWifiDialogListeners.add(privacyController2);
mWifiDialogListeners.add(meteredPreferenceController2);
这3个分别是WifiDetailPreferenceController2.java、WifiDetailPreferenceController2.java、WifiDetailPreferenceController2.java。
看一些这些类的submit方法。
@Override
public void onSubmit(WifiDialog2 dialog) {
Log.d("xz", "WifiMeteredPreferenceController2 onSubmit: ");
if (dialog.getController() != null && mWifiEntry.canSetMeteredChoice()) {
final WifiConfiguration newConfig = dialog.getController().getConfig();
if (newConfig == null) {
return;
}
Log.d("xz", "WifiMeteredPreferenceController2 onSubmit: getWifiEntryMeteredChoice(newConfig) = " + getWifiEntryMeteredChoice(newConfig)
+ " mWifiEntry.getMeteredChoice() = " + mWifiEntry.getMeteredChoice());
if (getWifiEntryMeteredChoice(newConfig) != mWifiEntry.getMeteredChoice()) {
mWifiEntry.setMeteredChoice(getWifiEntryMeteredChoice(newConfig));
onPreferenceChange(mPreference, String.valueOf(newConfig.meteredOverride));
}
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (mWifiEntry.isSaved() || mWifiEntry.isSubscription()) {
mWifiEntry.setMeteredChoice(Integer.parseInt((String) newValue));
}
// Stage the backup of the SettingsProvider package which backs this up
BackupManager.dataChanged("com.android.providers.settings");
updateSummary((ListPreference) preference, getMeteredOverride());
return true;
}
@Override
public void onSubmit(WifiDialog2 dialog) {
Log.d("xz", "WifiPrivacyPreferenceController2 onSubmit: ");
if (dialog.getController() != null) {
final WifiConfiguration newConfig = dialog.getController().getConfig();
if (newConfig == null) {
return;
}
Log.d("xz", "WifiPrivacyPreferenceController2 getWifiEntryPrivacy(newConfig): " + getWifiEntryPrivacy(newConfig)
+ " mWifiEntry.getPrivacy() = " + mWifiEntry.getPrivacy());
if (getWifiEntryPrivacy(newConfig) != mWifiEntry.getPrivacy()) {
mWifiEntry.setPrivacy(getWifiEntryPrivacy(newConfig));
onPreferenceChange(mPreference, String.valueOf(newConfig.macRandomizationSetting));
}
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final int privacy = Integer.parseInt((String) newValue);
mWifiEntry.setPrivacy(privacy);
// To activate changing, we need to reconnect network. WiFi will auto connect to
// current network after disconnect(). Only needed when this is connected network.
if (mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
mWifiEntry.disconnect(null /* callback */);
mWifiEntry.connect(null /* callback */);
}
updateSummary((ListPreference) preference, privacy);
return true;
}
@Override
public void onSubmit(WifiDialog2 dialog) {
Log.d("xz", "WifiDetailPreferenceController2 onSubmit: ");
if (dialog.getController() != null) {
WifiConfiguration config = dialog.getController().getConfig();
Log.d("xz", "WifiDetailPreferenceController2 onSubmit: dialog.getController().getConfig() = " + config + " password = " + config.SSID);
mWifiManager.save(config, new WifiManager.ActionListener() {
@Override
public void onSuccess() {
}
@Override
public void onFailure(int reason) {
Activity activity = mFragment.getActivity();
if (activity != null) {
Toast.makeText(activity,
R.string.wifi_failed_save_message,
Toast.LENGTH_SHORT).show();
}
}
});
}
}
分析这3个方法,再结合现象找出可问题点。
是由于
mWifiEntry.setMeteredChoice()
mWifiEntry.setPrivacy()
这个方法的执行导致的,当改变 隐私和 流量这两个设置时,会导致这两个方法执行 。
这两个方法去看的时候看不到具体的细节。根据现象去分析,可能是这两个方法修改了mWifiEntry的密码导致的,但是 mWifiManager.save(config, new WifiManager.ActionListener() 此保存设置的方法的执行是有用的。所以解决方案就是将mWifiManager.save(config, new WifiManager.ActionListener() 方法的执行放在后面。
在WifiNetworkDetailsFragment.java中将他们的add顺序调整一下就可以了。
mWifiDialogListeners.add(privacyController2);
mWifiDialogListeners.add(meteredPreferenceController2);
mWifiDialogListeners.add(mWifiDetailPreferenceController2);