目录
3.系统frameworks禁止进入应用信息页的核心代码功能分析
3.1AppErrorDialog.java关于错误信息的分析
3.3 ContextImpl.java关于启动Activity的相关代码调用
3.4 Activity.java中关于禁止启动应用信息的相关代码
1.概述
在定制化开发中,在app崩溃后,会弹出弹窗提醒崩溃了,然后弹窗可以通过点击应用信息按钮进入应用信息页这对于权限的管控不好处理,所以要求frameworks层禁止通过activity进入应用详情页
2.系统frameworks禁止进入应用信息页的核心代码
frameworks/base/core/java/android/app/Activity.java
frameworks/base/core/java/android/app/ContextImpl.java
frameworks/base/services/core/java/com/android/server/am/AppErrorDialog.java
frameworks/base/services/core/java/com/android/server/am/AppErrors.java
3.系统frameworks禁止进入应用信息页的核心代码功能分析
3.1AppErrorDialog.java关于错误信息的分析
final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener {
private final ActivityManagerService mService;
private final AppErrorResult mResult;
private final ProcessRecord mProc;
private final boolean mIsRestartable;
static int CANT_SHOW = -1;
static int BACKGROUND_USER = -2;
static int ALREADY_SHOWING = -3;
// Event 'what' codes
static final int FORCE_QUIT = 1;
static final int FORCE_QUIT_AND_REPORT = 2;
static final int RESTART = 3;
static final int MUTE = 5;
static final int TIMEOUT = 6;
static final int CANCEL = 7;
static final int APP_INFO = 8;
// 5-minute timeout, then we automatically dismiss the crash dialog
static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
public AppErrorDialog(Context context, ActivityManagerService service, Data data) {
super(context);
Resources res = context.getResources();
mService = service;
mProc = data.proc;
mResult = data.result;
mIsRestartable = (data.taskId != INVALID_TASK_ID || data.isRestartableForService)
&& Settings.Global.getInt(context.getContentResolver(),
Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG, 0) != 0;
BidiFormatter bidi = BidiFormatter.getInstance();
CharSequence name;
if ((mProc.pkgList.size() == 1) &&
(name = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
setTitle(res.getString(
data.repeating ? com.android.internal.R.string.aerr_application_repeated
: com.android.internal.R.string.aerr_application,
bidi.unicodeWrap(name.toString()),
bidi.unicodeWrap(mProc.info.processName)));
} else {
name = mProc.processName;
setTitle(res.getString(
data.repeating ? com.android.internal.R.string.aerr_process_repeated
: com.android.internal.R.string.aerr_process,
bidi.unicodeWrap(name.toString())));
}
setCancelable(true);
setCancelMessage(mHandler.obtainMessage(CANCEL));
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.setTitle("Application Error: " + mProc.info.processName);
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
| WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
if (mProc.isPersistent()) {
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
}
// After the timeout, pretend the user clicked the quit button
mHandler.sendMessageDelayed(
mHandler.obtainMessage(TIMEOUT),
DISMISS_TIMEOUT);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final FrameLayout frame = findViewById(android.R.id.custom);
final Context context = getContext();
LayoutInflater.from(context).inflate(
com.android.internal.R.layout.app_error_dialog, frame, true);
final boolean hasReceiver = mProc.errorReportReceiver != null;
final TextView restart = findViewById(com.android.internal.R.id.aerr_restart);
restart.setOnClickListener(this);
restart.setVisibility(mIsRestartable ? View.VISIBLE : View.GONE);
final TextView report = findViewById(com.android.internal.R.id.aerr_report);
report.setOnClickListener(this);
report.setVisibility(hasReceiver ? View.VISIBLE : View.GONE);
final TextView close = findViewById(com.android.internal.R.id.aerr_close);
close.setOnClickListener(this);
final TextView appInfo = findViewById(com.android.internal.R.id.aerr_app_info);
appInfo.setOnClickListener(this);
boolean showMute = !Build.IS_USER && Settings.Global.getInt(context.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0
&& Settings.Global.getInt(context.getContentResolver(),
Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG, 0) != 0;
final TextView mute = findViewById(com.android.internal.R.id.aerr_mute);
mute.setOnClickListener(this);
mute.setVisibility(showMute ? View.VISIBLE : View.GONE);
findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE);
}
@Override
public void onStart() {
super.onStart();
getContext().registerReceiver(mReceiver,
new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
@Override
protected void onStop() {
super.onStop();
getContext().unregisterReceiver(mReceiver);
}
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
setResult(msg.what);
dismiss();
}
};
@Override
public void dismiss() {
if (!mResult.mHasResult) {
// We are dismissing and the result has not been set...go ahead and set.
setResult(FORCE_QUIT);
}
super.dismiss();
}
private void setResult(int result) {
synchronized (mService) {
if (mProc != null && mProc.crashDialog == AppErrorDialog.this) {
mProc.crashDialog = null;
}
}
mResult.set(result);
// Make sure we don't have time timeout still hanging around.
mHandler.removeMessages(TIMEOUT);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case com.android.internal.R.id.aerr_restart:
mHandler.obtainMessage(RESTART).sendToTarget();
break;
case com.android.internal.R.id.aerr_report:
mHandler.obtainMessage(FORCE_QUIT_AND_REPORT).sendToTarget();
break;
case com.android.internal.R.id.aerr_close:
mHandler.obtainMessage(FORCE_QUIT).sendToTarget();
break;
case com.android.internal.R.id.aerr_app_info:
mHandler.obtainMessage(APP_INFO).sendToTarget();
break;
case com.android.internal.R.id.aerr_mute:
mHandler.obtainMessage(MUTE).sendToTarget();
break;
default:
break;
}
}
private void setResult(int result) {
synchronized (mService) {
if (mProc != null && mProc.crashDialog == AppErrorDialog.this) {
mProc.crashDialog = null;
}
}
mResult.set(result);
// Make sure we don't have time timeout still hanging around.
mHandler.removeMessages(TIMEOUT);
}
final class AppErrorResult {
public void set(int res) {
synchronized (this) {
mHasResult = true;
mResult = res;
notifyAll();
}
}
public int get() {
synchronized (this) {
while (!mHasResult) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mResult;
}
boolean mHasResult = false;
int mResult;
}
在AppErrorDialog中,从源码中就是在app崩溃的时候,弹窗弹出停止运行和查看应用详情这两个选项,所以可以从查看应用详情的相关源码注释掉相关源码
3.2 AppErrors.java的相关代码
void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
crashApplicationInner(r, crashInfo, callingPid, callingUid);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
int callingPid, int callingUid) {
long timeMillis = System.currentTimeMillis();
String shortMsg = crashInfo.exceptionClassName;
String longMsg = crashInfo.exceptionMessage;
String stackTrace = crashInfo.stackTrace;
if (shortMsg != null && longMsg != null) {
longMsg = shortMsg + ": " + longMsg;
} else if (shortMsg != null) {
longMsg = shortMsg;
}
if (r != null) {
boolean isApexModule = false;
try {
for (String androidPackage : r.getPackageList()) {
ModuleInfo moduleInfo = mContext.getPackageManager().getModuleInfo(
androidPackage, /*flags=*/ 0);
if (moduleInfo != null) {
isApexModule = true;
break;
}
}
} catch (IllegalStateException | PackageManager.NameNotFoundException e) {
// Call to PackageManager#getModuleInfo() can result in NameNotFoundException or
// IllegalStateException. In case they are thrown, there isn't much we can do
// other than proceed with app crash handling.
}
if (r.isPersistent() || isApexModule) {
// If a persistent app or apex module is stuck in a crash loop, the device isn't
// very usable, so we want to consider sending out a rescue party.
RescueParty.noteAppCrash(mContext, r.uid);
}
mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode());
}
final int relaunchReason = r != null
? r.getWindowProcessController().computeRelaunchReason() : RELAUNCH_REASON_NONE;
AppErrorResult result = new AppErrorResult();
int taskId;
synchronized (mService) {
/**
* If crash is handled by instance of {@link android.app.IActivityController},
* finish now and don't show the app error dialog.
*/
if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
timeMillis, callingPid, callingUid)) {
return;
}
// Suppress crash dialog if the process is being relaunched due to a crash during a free
// resize.
if (relaunchReason == RELAUNCH_REASON_FREE_RESIZE) {
return;
}
/**
* If this process was running instrumentation, finish now - it will be handled in
* {@link ActivityManagerService#handleAppDiedLocked}.
*/
if (r != null && r.getActiveInstrumentation() != null) {
return;
}
// Log crash in battery stats.
if (r != null) {
mService.mBatteryStatsService.noteProcessCrash(r.processName, r.uid);
}
AppErrorDialog.Data data = new AppErrorDialog.Data();
data.result = result;
data.proc = r;
// If we can't identify the process or it's already exceeded its crash quota,
// quit right away without showing a crash dialog.
if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
return;
}
final Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
taskId = data.taskId;
msg.obj = data;
mService.mUiHandler.sendMessage(msg);
}
int res = result.get();
Intent appErrorIntent = null;
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
res = AppErrorDialog.FORCE_QUIT;
}
synchronized (mService) {
if (res == AppErrorDialog.MUTE) {
stopReportingCrashesLocked(r);
}
if (res == AppErrorDialog.RESTART) {
mService.mProcessList.removeProcessLocked(r, false, true, "crash");
if (taskId != INVALID_TASK_ID) {
try {
mService.startActivityFromRecents(taskId,
ActivityOptions.makeBasic().toBundle());
} catch (IllegalArgumentException e) {
// Hmm...that didn't work. Task should either be in recents or associated
// with a stack.
Slog.e(TAG, "Could not restart taskId=" + taskId, e);
}
}
}
if (res == AppErrorDialog.FORCE_QUIT) {
long orig = Binder.clearCallingIdentity();
try {
// Kill it with fire!
mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
if (!r.isPersistent()) {
mService.mProcessList.removeProcessLocked(r, false, false, "crash");
mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
}
} finally {
Binder.restoreCallingIdentity(orig);
}
}
if (res == AppErrorDialog.APP_INFO) {
appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
}
if (r != null && !r.isolated && res != AppErrorDialog.RESTART) {
// XXX Can't keep track of crash time for isolated processes,
// since they don't have a persistent identity.
mProcessCrashTimes.put(r.info.processName, r.uid,
SystemClock.uptimeMillis());
}
}
if (appErrorIntent != null) {
try {
mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "bug report receiver dissappeared", e);
}
}
}
从这段代码可以看出调用的是Settings.ACTION_APPLICATION_DETAILS_SETTINGS的intent应用信息页调转
if (res == AppErrorDialog.APP_INFO) {
appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
最后通过mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));来调整到应用信息页
3.3 ContextImpl.java关于启动Activity的相关代码调用
class ContextImpl extends Context {
private final static String TAG = "ContextImpl";
private final static boolean DEBUG = false;
private static final String XATTR_INODE_CACHE = "user.inode_cache";
private static final String XATTR_INODE_CODE_CACHE = "user.inode_code_cache";
/**
* Map from package name, to preference name, to cached preferences.
*/
@GuardedBy("ContextImpl.class")
@UnsupportedAppUsage
private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;
/**
* Map from preference name to generated path.
*/
@GuardedBy("ContextImpl.class")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private ArrayMap<String, File> mSharedPrefsPaths;
@UnsupportedAppUsage
final @NonNull ActivityThread mMainThread;
@UnsupportedAppUsage
final @NonNull LoadedApk mPackageInfo;
@UnsupportedAppUsage
private @Nullable ClassLoader mClassLoader;
private final @Nullable IBinder mActivityToken;
private final @NonNull UserHandle mUser;
@UnsupportedAppUsage
static ContextImpl getImpl(Context context) {
Context nextContext;
while ((context instanceof ContextWrapper) &&
(nextContext=((ContextWrapper)context).getBaseContext()) != null) {
context = nextContext;
}
return (ContextImpl)context;
}
@Override
public AssetManager getAssets() {
return getResources().getAssets();
}
@Override
public Resources getResources() {
return mResources;
}
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
@Override
public Looper getMainLooper() {
return mMainThread.getLooper();
}
@Override
public Executor getMainExecutor() {
return mMainThread.getExecutor();
}
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}
@Override
public void setTheme(int resId) {
synchronized (mSync) {
if (mThemeResource != resId) {
mThemeResource = resId;
initializeTheme();
}
}
}
@Override
public int getThemeResId() {
synchronized (mSync) {
return mThemeResource;
}
}
@Override
public Resources.Theme getTheme() {
synchronized (mSync) {
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(mThemeResource,
getOuterContext().getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
}
private void initializeTheme() {
if (mTheme == null) {
mTheme = mResources.newTheme();
}
mTheme.applyStyle(mThemeResource, true);
}
@Override
public ClassLoader getClassLoader() {
return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());
}
@Override
public String getPackageName() {
if (mPackageInfo != null) {
return mPackageInfo.getPackageName();
}
// No mPackageInfo means this is a Context for the system itself,
// and this here is its name.
return "android";
}
/** @hide */
@Override
public String getBasePackageName() {
return mBasePackageName != null ? mBasePackageName : getPackageName();
}
@Override
public void startActivity(Intent intent) {
// add core start
if(intent==null)return;
String action = intent.getAction();
if(DEBUG)Log.e("ContextImpl","startActivity--action:"+action);
if(action!=null&&action.equals("android.settings.APPLICATION_DETAILS_SETTINGS"))return;
//add core end
warnIfCallingFromSystemProcess();
startActivity(intent, null);
}
/** @hide */
@Override
public void startActivityAsUser(Intent intent, UserHandle user) {
// add core start
if(intent==null)return;
String action = intent.getAction();
if(DEBUG)Log.e("ContextImpl","startActivityAsUser--action:"+action);
if(action!=null&&action.equals("android.settings.APPLICATION_DETAILS_SETTINGS"))return;
//add core end
startActivityAsUser(intent, null, user);
}
}
在ContextImpl.java的startActivity(Intent intent)和startActivityAsUser(Intent intent, UserHandle user)中判断是否是关于应用信息的启动信息 然后禁止启动就行了
3.4 Activity.java中关于禁止启动应用信息的相关代码
/**
* Same as {@link #startActivity(Intent, Bundle)} with no options
* specified.
*
* @param intent The intent to start.
*
* @throws android.content.ActivityNotFoundException
*
* @see #startActivity(Intent, Bundle)
* @see #startActivityForResult
*/
@Override
public void startActivity(Intent intent) {
// add core start
if(intent==null)return;
String action = intent.getAction();
if(DEBUG_LIFECYCLE)Log.e("Activity","startActivity--action:"+action);
if(action!=null&&action.equals("android.settings.APPLICATION_DETAILS_SETTINGS"))return;
//add core end
this.startActivity(intent, null);
}
/**
* @hide Implement to provide correct calling token.
*/
@Override
public void startActivityAsUser(Intent intent, UserHandle user) {
// add core start
if(intent==null)return;
String action = intent.getAction();
if(DEBUG_LIFECYCLE)Log.e("Activity","action:"+action);
if(action!=null&&action.equals("android.settings.APPLICATION_DETAILS_SETTINGS"))return;
//add core end
startActivityAsUser(intent, null, user);
}
在Activity中的startActivity(Intent intent)和startActivityAsUser(Intent intent, UserHandle user)中判断是否是关于应用信息的启动信息 然后禁止启动就行了,这样就达到了禁用查看应用详情的功能