| package com.google.android.hiddenapi.testapp; |
| |
| import android.app.Activity; |
| import android.os.Bundle; |
| import android.os.StrictMode; |
| import android.os.strictmode.Violation; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| import android.view.ViewGroup; |
| import android.widget.Button; |
| import android.widget.TextView; |
| import dalvik.system.VMRuntime; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| |
| public class MainActivity extends Activity implements OnClickListener { |
| |
| enum AppStrictMode { |
| OFF, |
| LOG, |
| DEATH, |
| CALLBACK; |
| private static AppStrictMode[] vals = values(); |
| public AppStrictMode next() |
| { |
| return vals[(this.ordinal()+1) % vals.length]; |
| } |
| } |
| |
| private final StrictMode.OnVmViolationListener mViolationListener = (v) -> onVmViolation(v); |
| |
| private TextView mTextView; |
| private TextView mCurrentStrictMode; |
| private TextView mStrictModeText; |
| |
| private AppStrictMode mCurrentMode = AppStrictMode.OFF; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_main); |
| mTextView = findViewById(R.id.textView); |
| mCurrentStrictMode = findViewById(R.id.strict_mode); |
| mStrictModeText = findViewById(R.id.strict_mode_text); |
| |
| setAllButtonClickListeners(findViewById(R.id.layout)); |
| |
| setStrictMode(AppStrictMode.OFF); |
| } |
| |
| private void setAllButtonClickListeners(View v) { |
| if (v instanceof Button) { |
| v.setOnClickListener(this); |
| } else if (v instanceof ViewGroup) { |
| ViewGroup vg = (ViewGroup) v; |
| for (int i = 0; i < vg.getChildCount(); i++) { |
| setAllButtonClickListeners(vg.getChildAt(i)); |
| } |
| } |
| } |
| |
| private void onVmViolation(Violation v) { |
| setStrictModeText(toString(v)); |
| } |
| |
| private void setStrictModeText(String s) { |
| if (TextUtils.isEmpty(s)) { |
| mStrictModeText.setText(""); |
| mStrictModeText.setVisibility(View.GONE); |
| } else { |
| mStrictModeText.setText(s); |
| mStrictModeText.setVisibility(View.VISIBLE); |
| } |
| |
| } |
| private void setStrictMode(AppStrictMode mode) { |
| StrictMode.VmPolicy policy; |
| switch (mode) { |
| case OFF: |
| policy = new StrictMode.VmPolicy.Builder().permitNonSdkApiUsage().build(); |
| break; |
| case LOG: |
| policy = new StrictMode.VmPolicy.Builder().detectNonSdkApiUsage().penaltyLog().build(); |
| break; |
| case DEATH: |
| policy = new StrictMode.VmPolicy.Builder().detectNonSdkApiUsage().penaltyDeath().build(); |
| break; |
| case CALLBACK: |
| policy = new StrictMode.VmPolicy.Builder() |
| .detectNonSdkApiUsage() |
| .penaltyListener(r -> mStrictModeText.post(r), mViolationListener) |
| .build(); |
| break; |
| default: |
| return; |
| } |
| mCurrentMode = mode; |
| StrictMode.setVmPolicy(policy); |
| mCurrentStrictMode.setText("StrictMode: " + mode); |
| } |
| |
| @Override |
| public void onClick(View view) { |
| setStrictModeText(null); |
| switch (view.getId()) { |
| case R.id.disable_checks_reflection: |
| try { |
| tryDisableApiChecksReflectively(); |
| mTextView.setText("Ok \uD83D\uDE00"); |
| } catch (Exception e) { |
| handleException(e); |
| } |
| break; |
| case R.id.disable_checks_linking: |
| try { |
| tryDisableApiChecksLinking(); |
| } catch (LinkageError e) { |
| handleException(e); |
| } |
| break; |
| case R.id.strict_mode: |
| setStrictMode(mCurrentMode.next()); |
| break; |
| default: |
| String name = ((TextView) view).getText().toString(); |
| accessActivityMember(name); |
| break; |
| } |
| } |
| |
| private void accessActivityMember(String name) { |
| try { |
| Field f = Activity.class.getDeclaredField(name); |
| f.setAccessible(true); |
| Object o = f.get(this); |
| mTextView.setText(String.format("%s: %s", name, o == null ? "null" : o.toString())); |
| } catch (NoSuchFieldException e) { |
| handleException(e); |
| } catch (IllegalAccessException e) { |
| handleException(e); |
| } |
| } |
| |
| private void handleException(Throwable e) { |
| Log.e(getClass().getSimpleName(), "Failed", e); |
| mTextView.setText(toString(e)); |
| } |
| |
| private static String toString(Throwable t) { |
| StringWriter s = new StringWriter(); |
| t.printStackTrace(new PrintWriter(s)); |
| return s.toString(); |
| } |
| |
| private static void tryDisableApiChecksReflectively() |
| throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, |
| IllegalAccessException { |
| Class vmRuntimeClass = Class.forName("dalvik.system.VMRuntime"); |
| Method getRuntime = vmRuntimeClass.getDeclaredMethod("getRuntime"); |
| Object vmRuntime = getRuntime.invoke(null); |
| Method setExemptions = vmRuntimeClass.getDeclaredMethod( |
| "setHiddenApiExemptions", String[].class); |
| setExemptions.invoke(vmRuntime, (Object) new String[]{"L"}); |
| } |
| |
| private static void tryDisableApiChecksLinking() { |
| VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"}); |
| } |
| |
| } |