快过年了,被疫情困在家,实在无聊就给小米9换了个MIUI12_Global 的ROM。但是,每次开启USB调试,以及允许安装应用都需要等待5秒才能授权。于是楼主花费了几个钟头的时间终于搞定了五秒的等待时间,虽然只测试了小米9的V12.0.4.0.QFAMIXM,但是其他机型或者其他版本的ROM应该都差不多,感兴趣的可以测试一下。
机型 | 小米9(cepheus) |
ROM | V12.0.4.0.QFAMIXM_20210115.0000.00_10.0_global |
相关软件 | 设置(/system/product/priv-app/Settings/Settings.apk) |
手机管家(/system/priv-app/SecurityCenter/SecurityCenter.apk) |
1、定位软件:当前进程(名字就叫当前进程)
我在芥子空间找到了app【当前进程】用来查看当前窗口Activity的软件。
首先打开软件,然后开启悬浮窗进行记录Activity活动,
一看这个Activity的名字立马感觉就来了。
二话不说,把SecurityCenter.apk拿出用jd-gui来看看。
2、分析
找到com.miui.permcenter.privacymanager.SpecialPermissionInterceptActivity
有第一张图我们可以看到这个Activity的界面有两个Button,而且其中Button(Text="允许")需要计时结束后,才可点击,但是Button(Text="取消")则是随时可点击的。
用jd-gui打开com.miui.permcenter.privacymanager.SpecialPermissionInterceptActivity的源码:
package com.miui.permcenter.privacymanager;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import b.b.f.j.B;
import b.b.f.j.F;
import b.b.f.j.o;
import com.miui.permcenter.compact.SystemPropertiesCompat;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import miui.app.Activity;
public class SpecialPermissionInterceptActivity extends Activity implements View.OnClickListener {
private static final Map<String, b> a = new HashMap<String, b>();
private a b;
private String c;
private String d;
private String e;
private TextView f;
private LinearLayout g;
private TextView h;
private Button i;
private Button j;
private int k;
static {
a.put("perm_install_unknown", new b(2131757339, 2130903079, 2131757335));
a.put("perm_notification", new b(2131757336, 2130903077, 2131757337));
a.put("perm_app_statistics", new b(2131757338, 2130903078, 2131757337));
a.put("miui_open_debug", new b(2131755938, 2130903055, 2131755937));
a.put("miui_close_optimization", new b(2131757023, 2130903063, 2131757022));
a.put("oaid_close", new b(0, 2131757158, 2131757159));
}
private void a() {
try {
Window window = getWindow();
window.addFlags(-2147483648);
window.getDecorView().setSystemUiVisibility(768);
window.getClass().getMethod("setNavigationBarColor", new Class[] { int.class }).invoke(window, new Object[] { Integer.valueOf(0) });
} catch (Exception exception) {}
}
private void a(boolean paramBoolean) {
if ("miui_open_debug".equals(this.d) && paramBoolean) {
Settings.Global.putInt(getContentResolver(), "adb_enabled", paramBoolean);
} else if ("miui_close_optimization".equals(this.d) && paramBoolean) {
SystemPropertiesCompat.set("persist.sys.miui_optimization", Boolean.valueOf(paramBoolean ^ true).toString());
}
if (paramBoolean) {
byte b = -1;
} else {
paramBoolean = false;
}
setResult(paramBoolean); //设置Activity的Result,现在还用不到,但是在分析设置的时候很有用
finish();
}
private void b() {
//这个方法就是对这个Activity窗口不同的权限请求的判断
String[] arrayOfString;
if (this.d.startsWith("perm") && !TextUtils.isEmpty(this.e)) {//判断是要打开usb还是未知来源的安装请求。
this.f.setText(getString(((b)a.get(this.d)).a, new Object[] { B.j((Context)this, this.c) }));
this.h.setText(getString(((b)a.get(this.d)).c, new Object[] { this.e }));
} else if (this.d.startsWith("miui")) {
this.f.setText(((b)a.get(this.d)).a);
this.h.setText(((b)a.get(this.d)).c);
}
if ("oaid_close".equals(this.d)) {
this.f.setVisibility(4);
this.h.setText(((b)a.get(this.d)).c);
arrayOfString = new String[1];
arrayOfString[0] = getString(((b)a.get(this.d)).b);
} else {
arrayOfString = getResources().getStringArray(((b)a.get(this.d)).b);
}
LayoutInflater layoutInflater = LayoutInflater.from((Context)this);
int i = arrayOfString.length;
for (byte b = 0; b < i; b++) {
String str = arrayOfString[b];
View view = layoutInflater.inflate(2131493425, (ViewGroup)this.g, false);
TextView textView = (TextView)view.findViewById(2131297085);
if (arrayOfString.length == 1) {
view.findViewById(2131297086).setVisibility(8);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(textView.getLayoutParams());
layoutParams.leftMargin = 0;
textView.setLayoutParams((ViewGroup.LayoutParams)layoutParams);
}
textView.setText(str);
this.g.addView(view);
}
this.i.setText(getString(2131755748, new Object[] { Integer.valueOf(this.k) }));
this.b.sendEmptyMessageDelayed(1, 1000L);
}
private void c() {
int i = --this.k; //前面已经将k设为5,这里对k的值进行递减
if (i <= 0) { //判断是否小于0,如果小于0,则this.i.setEnabled(true);
this.i.setText(2131755747);
this.i.setEnabled(true); //允许 按键被设为可点击。
this.k = 0;
} else {
this.i.setText(getString(2131755748, new Object[] { Integer.valueOf(i) }));
this.b.removeMessages(1);
this.b.sendEmptyMessageDelayed(1, 1000L); // 时间为1秒
}
}
public void onBackPressed() {}
public void onClick(View paramView) {
//这个方法就是两个按钮共同绑定的方法,用户带年纪允许或者取消都会调用这个方法
boolean bool;
int i = paramView.getId();
if (i != 2131297087) { //判断i来确定用户点击了允许还是取消,并且将bool赋值(允许true)
if (i != 2131297091) //查找一下id可以找到相关信息,这里就跳过了
return;
bool = false;
} else {
bool = true;
}
a(bool); //当用户点击允许,这调用a(bool),这里的bool为true;
}
protected void onCreate(Bundle paramBundle) { // 1.先看这个方法
super.onCreate(paramBundle);
Window window = getWindow();
window.setBackgroundDrawable((Drawable)new ColorDrawable(getResources().getColor(2131100554)));
window.setLayout(-1, -1);
window.addFlags(4);
setContentView(2131493424);
a();
this.c = getIntent().getStringExtra("pkgName");
this.d = getIntent().getStringExtra("permName");
this.e = getIntent().getStringExtra("permDesc");
if (!a.containsKey(this.d)) {
finish();
return;
}
if (paramBundle != null) {
this.k = paramBundle.getInt("KET_STEP_COUNT", 5); //可以合理的怀疑一下k是不是那5秒
} else {
this.k = 5;
}
this.b = new a(this);
this.f = (TextView)findViewById(2131297090);
this.g = (LinearLayout)findViewById(2131297088);
this.h = (TextView)findViewById(2131297089);
this.i = (Button)findViewById(2131297087);//这里的this.i和this.j其实就是两个Button组件
this.j = (Button)findViewById(2131297091);
this.i.setEnabled(false);//这里按钮i被设置为不可点击状态,可以断定这个就是“取消”按钮
this.i.setOnClickListener(this); //两个按钮绑定了同一个OnClick
this.j.setOnClickListener(this);
if (F.a()) {
o.b((View)this.i);
o.b((View)this.j);
}
b();//跟进看一下
}
protected void onDestroy() {
super.onDestroy();
a a1 = this.b;
if (a1 != null)
a1.removeMessages(1);
}
protected void onNewIntent(Intent paramIntent) {
super.onNewIntent(paramIntent);
}
protected void onSaveInstanceState(Bundle paramBundle) {
super.onSaveInstanceState(paramBundle);
paramBundle.putInt("KET_STEP_COUNT", this.k);
}
private static class a extends Handler {
private WeakReference<SpecialPermissionInterceptActivity> a;
public a(SpecialPermissionInterceptActivity param1SpecialPermissionInterceptActivity) {
this.a = new WeakReference<SpecialPermissionInterceptActivity>(param1SpecialPermissionInterceptActivity);
}
public void handleMessage(Message param1Message) {
super.handleMessage(param1Message);
SpecialPermissionInterceptActivity specialPermissionInterceptActivity = this.a.get();
if (specialPermissionInterceptActivity != null && !specialPermissionInterceptActivity.isFinishing() && !specialPermissionInterceptActivity.isDestroyed())
SpecialPermissionInterceptActivity.a(specialPermissionInterceptActivity);
}
}
public static class b {
int a;
int b;
int c;
public b(int param1Int1, int param1Int2, int param1Int3) {
this.a = param1Int1;
this.b = param1Int2;
this.c = param1Int3;
}
}
}
分析到这里,想要去掉这5秒以及是手到擒来的事了。我们可以把k的值在onCreate方法中变成负数,或者将Button i 设置为setEnabled(true);即让它直接可以点击。
。。。。。。。
如果仅仅在SpecialPermissionInterceptActivity中修改它的组件属性就任然会有第一张图示的提示消息。楼主进一步琢磨着,准备向魔爪伸向Settings.apk。
用当前进程App 可以看到
当用户进入这个界面(com.android,settings.Settings$ManageAppExternalSourcesActivity),点击“允许来自此来源的应用” 就会启动com.miui.permcenter.privacymanager.SpecialPermissionInterceptActivity
所以在Settings.apk(包名:com.android,settings),启动SecurityCenter.apk(包名:com.miui.securitycenter)
我们可以在Settings.apk来查找相关的源码,查找是否有与com.miui.securitycenter相关的startActivity()方法。
经过人肉筛选,我定位到
com.android.settings.applications.appinfo.ExternalSourcesDetails
发现ExternalSourcesDetails类中有个fV()方法会启动com.miui.securitycenter的Activity
Intent intent = new Intent("com.miui.securitycenter.action.UNKNOWN_SOURCE_VERIFY");
intent.setPackage("com.miui.securitycenter");
startActivityForResult(intent, 101);
所以当fV()被调用,向com.miui.securitycenter发送intent,然后就会弹出SpecialPermissionInterceptActivity重要警告的窗口,
这里面有个十分关键的方法:
onPreferenceChange() 返回boolean值
这个方法就是当我们点击按钮后响应的方法,它的返回值决定了按钮是否改变。(return false 不改变按钮状态,return true 则将按钮改为相反的状态)
当我们点击“允许来自此来源的应用”就会弹出SpecialPermissionInterceptActivity重要警告的窗口,所以我们对onPreferenceChange()进行分析:
public boolean onPreferenceChange(Preference paramPreference, Object paramObject) {
boolean bool = ((Boolean)paramObject).booleanValue();
if (paramPreference == this.Ps) {
AppStateInstallAppsBridge.InstallAppsState installAppsState = this.Ws;
if (installAppsState != null && bool != installAppsState.gx()) {
if (bool) {
Intent intent = a.c(((AppInfoBase)this).mPackageName, "perm_install_unknown", this.mContext.getResources().getString(2131889241));
if (a.g(this.mContext, intent)) {
We(102);
return true;
}
if (fV())
return true;
}
We(bool);
}
return true;
}
return false;
}
我们看这个bool 的值实际上就是onPreferenceChange()第二个实际参数强制转换成的boolean值,
(1)如果bool = true;则会调用fV()启动警告窗口,改变按钮状态
(2)如果bool = false;则会执行We(bool);再看一下
onPreferenceChange()中调用了fV()方法,如果返回真值,就改变按钮状态。然后将bool作为参数执行We()方法
private void We(boolean paramBoolean) {
if (Settings.ManageAppExternalSourcesActivity.class.getName().equals(getIntent().getComponent().getClassName())) {
boolean bool;
if (paramBoolean) {
bool = true;
} else {
bool = false;
}
setResult(bool);
}
setCanInstallApps(paramBoolean);
refreshUi();
}
其实很容易看出来,实际上
We(boolean paramBoolean)方法的参数传递给setCanInstallApps()方法。
然后未知来源的安装权限就到手了。
我们可以在onPreferenceChange()方法中将调用fV()方法的代码注释掉,换成执行We(true)的代码就可以完成修改了。
package com.android.settings.applications.appinfo;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.preference.Preference;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.Settings;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.AppInfoWithHeader;
import com.android.settings.applications.AppStateInstallAppsBridge;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.applications.ApplicationsState;
import d.a.a.a;
import miui.os.Build;
public class ExternalSourcesDetails extends AppInfoWithHeader implements Preference.b {
private RestrictedSwitchPreference Ps;
private AppStateInstallAppsBridge Vs;
private AppStateInstallAppsBridge.InstallAppsState Ws;
private ActivityManager mActivityManager;
private AppOpsManager mAppOpsManager;
private Context mContext;
private UserManager mUserManager;
private void Ii(int paramInt) {
if (UserHandle.isCore(paramInt))
return;
this.mActivityManager.killUid(paramInt, "User denied OP_REQUEST_INSTALL_PACKAGES");
}
private void We(boolean paramBoolean) {
if (Settings.ManageAppExternalSourcesActivity.class.getName().equals(getIntent().getComponent().getClassName())) {
boolean bool;
if (paramBoolean) {
bool = true;
} else {
bool = false;
}
setResult(bool);
}
setCanInstallApps(paramBoolean);
refreshUi();
}
public static CharSequence b(Context paramContext, ApplicationsState.a parama) {
UserHandle userHandle = UserHandle.getUserHandleForUid(parama.info.uid);
UserManager userManager = UserManager.get(paramContext);
int i = userManager.getUserRestrictionSource("no_install_unknown_sources", userHandle);
i = userManager.getUserRestrictionSource("no_install_unknown_sources_globally", userHandle) | i;
if ((i & 0x1) != 0)
return paramContext.getString(2131888227);
if (i != 0)
return paramContext.getString(2131888224);
if ((new AppStateInstallAppsBridge(paramContext, null, null)).s(parama.info.packageName, parama.info.uid).gx()) {
i = 2131886579;
} else {
i = 2131886580;
}
return paramContext.getString(i);
}
private boolean fV() {
if (!Build.IS_INTERNATIONAL_BUILD && !Build.IS_TABLET && UserHandle.myUserId() == 0) {
Intent intent = new Intent("com.miui.securitycenter.action.UNKNOWN_SOURCE_VERIFY");
intent.setPackage("com.miui.securitycenter");
startActivityForResult(intent, 101);
return true;
}
return false;
}
public int ab() {
return 808;
}
protected AlertDialog k(int paramInt1, int paramInt2) {
return null;
}
public void onActivityResult(int paramInt1, int paramInt2, Intent paramIntent) {
super.onActivityResult(paramInt1, paramInt2, paramIntent);
boolean bool1 = false;
boolean bool2 = false;
if (paramInt1 == 101) {
if (paramInt2 == -1)
bool2 = true;
We(bool2);
} else if (paramInt1 == 102) {
bool2 = bool1;
if (paramInt2 == -1)
bool2 = true;
AppStateInstallAppsBridge.InstallAppsState installAppsState = this.Ws;
if (installAppsState != null && bool2 != installAppsState.gx()) {
if (bool2 && fV())
return;
We(true);
}
}
}
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
Activity activity = getActivity();
this.Vs = new AppStateInstallAppsBridge((Context)activity, ((AppInfoBase)this).mState, null);
this.mAppOpsManager = (AppOpsManager)activity.getSystemService("appops");
this.mActivityManager = (ActivityManager)activity.getSystemService(ActivityManager.class);
this.mUserManager = UserManager.get((Context)activity);
addPreferencesFromResource(2132082829);
this.Ps = (RestrictedSwitchPreference)findPreference("external_sources_settings_switch");
this.Ps.setOnPreferenceChangeListener(this);
this.mContext = (Context)activity;
}
public void onDestroy() {
this.Vs.release();
super.onDestroy();
}
public boolean onPreferenceChange(Preference paramPreference, Object paramObject) {
boolean bool = ((Boolean)paramObject).booleanValue();
if (paramPreference == this.Ps) {
AppStateInstallAppsBridge.InstallAppsState installAppsState = this.Ws;
if (installAppsState != null && bool != installAppsState.gx()) {
if (bool) {
Intent intent = a.c(((AppInfoBase)this).mPackageName, "perm_install_unknown", this.mContext.getResources().getString(2131889241));
if (a.g(this.mContext, intent)) {
We(102);
return true;
}
if (fV())
return true;
}
We(bool);
}
return true;
}
return false;
}
protected boolean refreshUi() {
PackageInfo packageInfo = ((AppInfoBase)this).mPackageInfo;
if (packageInfo == null || packageInfo.applicationInfo == null)
return false;
if (this.mUserManager.hasBaseUserRestriction("no_install_unknown_sources", UserHandle.of(UserHandle.myUserId()))) {
this.Ps.setChecked(false);
this.Ps.setSummary(2131888224);
this.Ps.setEnabled(false);
return true;
}
this.Ps.Ba("no_install_unknown_sources");
if (!this.Ps.zj())
this.Ps.Ba("no_install_unknown_sources_globally");
if (this.Ps.zj())
return true;
this.Ws = this.Vs.s(((AppInfoBase)this).mPackageName, ((AppInfoBase)this).mPackageInfo.applicationInfo.uid);
if (!this.Ws.isPotentialAppSource()) {
this.Ps.setEnabled(false);
return true;
}
this.Ps.setChecked(this.Ws.gx());
return true;
}
@VisibleForTesting
void setCanInstallApps(boolean paramBoolean) {
byte b1;
AppOpsManager appOpsManager = this.mAppOpsManager;
int i = ((AppInfoBase)this).mPackageInfo.applicationInfo.uid;
String str = ((AppInfoBase)this).mPackageName;
if (paramBoolean) {
b1 = 0;
} else {
b1 = 2;
}
appOpsManager.setMode(66, i, str, b1);
if (!paramBoolean)
Ii(((AppInfoBase)this).mPackageInfo.applicationInfo.uid);
}
}
ps:关于如何定位到代码,就自己慢慢琢磨吧,这是很难解释清楚的。